diff options
Diffstat (limited to 'compiler/rustc_middle/src')
27 files changed, 856 insertions, 547 deletions
diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index 672073b1d34..9a42bbe7bac 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -32,6 +32,7 @@ macro_rules! arena_types { [decode] borrowck_result: rustc_middle::mir::BorrowCheckResult<$tcx>, [decode] unsafety_check_result: rustc_middle::mir::UnsafetyCheckResult, + [decode] code_region: rustc_middle::mir::coverage::CodeRegion, [] const_allocs: rustc_middle::mir::interpret::Allocation, // Required for the incremental on-disk cache [few] mir_keys: rustc_hir::def_id::DefIdSet, diff --git a/compiler/rustc_middle/src/dep_graph/dep_node.rs b/compiler/rustc_middle/src/dep_graph/dep_node.rs index a61b9af9bac..d954c8ab5fb 100644 --- a/compiler/rustc_middle/src/dep_graph/dep_node.rs +++ b/compiler/rustc_middle/src/dep_graph/dep_node.rs @@ -193,6 +193,15 @@ macro_rules! define_dep_nodes { pub type DepNode = rustc_query_system::dep_graph::DepNode<DepKind>; + // We keep a lot of `DepNode`s in memory during compilation. It's not + // required that their size stay the same, but we don't want to change + // it inadvertently. This assert just ensures we're aware of any change. + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + static_assert_size!(DepNode, 17); + + #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] + static_assert_size!(DepNode, 24); + pub trait DepNodeExt: Sized { /// Construct a DepNode from the given DepKind and DefPathHash. This /// method will assert that the given DepKind actually requires a @@ -227,7 +236,7 @@ macro_rules! define_dep_nodes { debug_assert!(kind.can_reconstruct_query_key() && kind.has_params()); DepNode { kind, - hash: def_path_hash.0, + hash: def_path_hash.0.into(), } } @@ -243,8 +252,7 @@ macro_rules! define_dep_nodes { /// has been removed. fn extract_def_id(&self, tcx: TyCtxt<'tcx>) -> Option<DefId> { if self.kind.can_reconstruct_query_key() { - let def_path_hash = DefPathHash(self.hash); - tcx.def_path_hash_to_def_id.as_ref()?.get(&def_path_hash).cloned() + tcx.queries.on_disk_cache.as_ref()?.def_path_hash_to_def_id(tcx, DefPathHash(self.hash.into())) } else { None } @@ -311,7 +319,17 @@ impl<'tcx> DepNodeParams<TyCtxt<'tcx>> for DefId { } fn to_fingerprint(&self, tcx: TyCtxt<'tcx>) -> Fingerprint { - tcx.def_path_hash(*self).0 + let hash = tcx.def_path_hash(*self); + // If this is a foreign `DefId`, store its current value + // in the incremental cache. When we decode the cache, + // we will use the old DefIndex as an initial guess for + // a lookup into the crate metadata. + if !self.is_local() { + if let Some(cache) = &tcx.queries.on_disk_cache { + cache.store_foreign_def_id_hash(*self, hash); + } + } + hash.0 } fn to_debug_str(&self, tcx: TyCtxt<'tcx>) -> String { @@ -350,7 +368,7 @@ impl<'tcx> DepNodeParams<TyCtxt<'tcx>> for CrateNum { fn to_fingerprint(&self, tcx: TyCtxt<'tcx>) -> Fingerprint { let def_id = DefId { krate: *self, index: CRATE_DEF_INDEX }; - tcx.def_path_hash(def_id).0 + def_id.to_fingerprint(tcx) } fn to_debug_str(&self, tcx: TyCtxt<'tcx>) -> String { diff --git a/compiler/rustc_middle/src/dep_graph/mod.rs b/compiler/rustc_middle/src/dep_graph/mod.rs index 66975242798..e641c1cd77b 100644 --- a/compiler/rustc_middle/src/dep_graph/mod.rs +++ b/compiler/rustc_middle/src/dep_graph/mod.rs @@ -5,7 +5,7 @@ use rustc_data_structures::profiling::SelfProfilerRef; use rustc_data_structures::sync::Lock; use rustc_data_structures::thin_vec::ThinVec; use rustc_errors::Diagnostic; -use rustc_hir::def_id::LocalDefId; +use rustc_hir::def_id::{DefPathHash, LocalDefId}; mod dep_node; @@ -91,6 +91,12 @@ impl<'tcx> DepContext for TyCtxt<'tcx> { type DepKind = DepKind; type StableHashingContext = StableHashingContext<'tcx>; + fn register_reused_dep_path_hash(&self, hash: DefPathHash) { + if let Some(cache) = self.queries.on_disk_cache.as_ref() { + cache.register_reused_dep_path_hash(*self, hash) + } + } + fn create_stable_hashing_context(&self) -> Self::StableHashingContext { TyCtxt::create_stable_hashing_context(*self) } @@ -164,11 +170,17 @@ impl<'tcx> DepContext for TyCtxt<'tcx> { } fn load_diagnostics(&self, prev_dep_node_index: SerializedDepNodeIndex) -> Vec<Diagnostic> { - self.queries.on_disk_cache.load_diagnostics(*self, prev_dep_node_index) + self.queries + .on_disk_cache + .as_ref() + .map(|c| c.load_diagnostics(*self, prev_dep_node_index)) + .unwrap_or_default() } fn store_diagnostics(&self, dep_node_index: DepNodeIndex, diagnostics: ThinVec<Diagnostic>) { - self.queries.on_disk_cache.store_diagnostics(dep_node_index, diagnostics) + if let Some(c) = self.queries.on_disk_cache.as_ref() { + c.store_diagnostics(dep_node_index, diagnostics) + } } fn store_diagnostics_for_anon_node( @@ -176,7 +188,9 @@ impl<'tcx> DepContext for TyCtxt<'tcx> { dep_node_index: DepNodeIndex, diagnostics: ThinVec<Diagnostic>, ) { - self.queries.on_disk_cache.store_diagnostics_for_anon_node(dep_node_index, diagnostics) + if let Some(c) = self.queries.on_disk_cache.as_ref() { + c.store_diagnostics_for_anon_node(dep_node_index, diagnostics) + } } fn profiler(&self) -> &SelfProfilerRef { diff --git a/compiler/rustc_middle/src/hir/map/collector.rs b/compiler/rustc_middle/src/hir/map/collector.rs index 516c9b6752b..82cfca4f171 100644 --- a/compiler/rustc_middle/src/hir/map/collector.rs +++ b/compiler/rustc_middle/src/hir/map/collector.rs @@ -112,6 +112,7 @@ impl<'a, 'hir> NodeCollector<'a, 'hir> { items: _, trait_items: _, impl_items: _, + foreign_items: _, bodies: _, trait_impls: _, body_ids: _, @@ -319,6 +320,10 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { self.visit_impl_item(self.krate.impl_item(item_id)); } + fn visit_nested_foreign_item(&mut self, foreign_id: ForeignItemId) { + self.visit_foreign_item(self.krate.foreign_item(foreign_id)); + } + fn visit_nested_body(&mut self, id: BodyId) { self.visit_body(self.krate.body(id)); } @@ -351,11 +356,17 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { }); } - fn visit_foreign_item(&mut self, foreign_item: &'hir ForeignItem<'hir>) { - self.insert(foreign_item.span, foreign_item.hir_id, Node::ForeignItem(foreign_item)); + fn visit_foreign_item(&mut self, fi: &'hir ForeignItem<'hir>) { + debug_assert_eq!( + fi.hir_id.owner, + self.definitions.opt_hir_id_to_local_def_id(fi.hir_id).unwrap() + ); + self.with_dep_node_owner(fi.hir_id.owner, fi, |this, hash| { + this.insert_with_hash(fi.span, fi.hir_id, Node::ForeignItem(fi), hash); - self.with_parent(foreign_item.hir_id, |this| { - intravisit::walk_foreign_item(this, foreign_item); + this.with_parent(fi.hir_id, |this| { + intravisit::walk_foreign_item(this, fi); + }); }); } @@ -561,6 +572,14 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { self.visit_nested_impl_item(id); } + + fn visit_foreign_item_ref(&mut self, fi: &'hir ForeignItemRef<'hir>) { + // Do not visit the duplicate information in ForeignItemRef. We want to + // map the actual nodes, not the duplicate ones in the *Ref. + let ForeignItemRef { id, ident: _, span: _, vis: _ } = *fi; + + self.visit_nested_foreign_item(id); + } } struct HirItemLike<T> { diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index d86e8987195..598e28c1a3a 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -47,7 +47,7 @@ fn fn_decl<'hir>(node: Node<'hir>) -> Option<&'hir FnDecl<'hir>> { } } -fn fn_sig<'hir>(node: Node<'hir>) -> Option<&'hir FnSig<'hir>> { +pub fn fn_sig<'hir>(node: Node<'hir>) -> Option<&'hir FnSig<'hir>> { match &node { Node::Item(Item { kind: ItemKind::Fn(sig, _, _), .. }) | Node::TraitItem(TraitItem { kind: TraitItemKind::Fn(sig, _), .. }) @@ -205,7 +205,7 @@ impl<'hir> Map<'hir> { ItemKind::TraitAlias(..) => DefKind::TraitAlias, ItemKind::ExternCrate(_) => DefKind::ExternCrate, ItemKind::Use(..) => DefKind::Use, - ItemKind::ForeignMod(..) => DefKind::ForeignMod, + ItemKind::ForeignMod { .. } => DefKind::ForeignMod, ItemKind::GlobalAsm(..) => DefKind::GlobalAsm, ItemKind::Impl { .. } => DefKind::Impl, }, @@ -309,6 +309,13 @@ impl<'hir> Map<'hir> { } } + pub fn foreign_item(&self, id: ForeignItemId) -> &'hir ForeignItem<'hir> { + match self.find(id.hir_id).unwrap() { + Node::ForeignItem(item) => item, + _ => bug!(), + } + } + pub fn body(&self, id: BodyId) -> &'hir Body<'hir> { self.tcx.hir_owner_nodes(id.hir_id.owner).unwrap().bodies.get(&id.hir_id.local_id).unwrap() } @@ -470,6 +477,10 @@ impl<'hir> Map<'hir> { for id in &module.impl_items { visitor.visit_impl_item(self.expect_impl_item(id.hir_id)); } + + for id in &module.foreign_items { + visitor.visit_foreign_item(self.expect_foreign_item(id.hir_id)); + } } /// Retrieves the `Node` corresponding to `id`, panicking if it cannot be found. @@ -718,10 +729,11 @@ impl<'hir> Map<'hir> { let parent = self.get_parent_item(hir_id); if let Some(entry) = self.find_entry(parent) { if let Entry { - node: Node::Item(Item { kind: ItemKind::ForeignMod(ref nm), .. }), .. + node: Node::Item(Item { kind: ItemKind::ForeignMod { abi, .. }, .. }), + .. } = entry { - return nm.abi; + return *abi; } } bug!("expected foreign mod or inlined parent, found {}", self.node_to_string(parent)) @@ -806,25 +818,34 @@ impl<'hir> Map<'hir> { /// Given a node ID, gets a list of attributes associated with the AST /// corresponding to the node-ID. pub fn attrs(&self, id: HirId) -> &'hir [ast::Attribute] { - let attrs = match self.find_entry(id).map(|entry| entry.node) { - Some(Node::Param(a)) => Some(&a.attrs[..]), - Some(Node::Local(l)) => Some(&l.attrs[..]), - Some(Node::Item(i)) => Some(&i.attrs[..]), - Some(Node::ForeignItem(fi)) => Some(&fi.attrs[..]), - Some(Node::TraitItem(ref ti)) => Some(&ti.attrs[..]), - Some(Node::ImplItem(ref ii)) => Some(&ii.attrs[..]), - Some(Node::Variant(ref v)) => Some(&v.attrs[..]), - Some(Node::Field(ref f)) => Some(&f.attrs[..]), - Some(Node::Expr(ref e)) => Some(&*e.attrs), - Some(Node::Stmt(ref s)) => Some(s.kind.attrs(|id| self.item(id.id))), - Some(Node::Arm(ref a)) => Some(&*a.attrs), - Some(Node::GenericParam(param)) => Some(¶m.attrs[..]), + let attrs = self.find_entry(id).map(|entry| match entry.node { + Node::Param(a) => &a.attrs[..], + Node::Local(l) => &l.attrs[..], + Node::Item(i) => &i.attrs[..], + Node::ForeignItem(fi) => &fi.attrs[..], + Node::TraitItem(ref ti) => &ti.attrs[..], + Node::ImplItem(ref ii) => &ii.attrs[..], + Node::Variant(ref v) => &v.attrs[..], + Node::Field(ref f) => &f.attrs[..], + Node::Expr(ref e) => &*e.attrs, + Node::Stmt(ref s) => s.kind.attrs(|id| self.item(id.id)), + Node::Arm(ref a) => &*a.attrs, + Node::GenericParam(param) => ¶m.attrs[..], // Unit/tuple structs/variants take the attributes straight from // the struct/variant definition. - Some(Node::Ctor(..)) => return self.attrs(self.get_parent_item(id)), - Some(Node::Crate(item)) => Some(&item.attrs[..]), - _ => None, - }; + Node::Ctor(..) => self.attrs(self.get_parent_item(id)), + Node::Crate(item) => &item.attrs[..], + Node::MacroDef(def) => def.attrs, + Node::AnonConst(..) + | Node::PathSegment(..) + | Node::Ty(..) + | Node::Pat(..) + | Node::Binding(..) + | Node::TraitRef(..) + | Node::Block(..) + | Node::Lifetime(..) + | Node::Visibility(..) => &[], + }); attrs.unwrap_or(&[]) } @@ -928,6 +949,10 @@ impl<'hir> intravisit::Map<'hir> for Map<'hir> { fn impl_item(&self, id: ImplItemId) -> &'hir ImplItem<'hir> { self.impl_item(id) } + + fn foreign_item(&self, id: ForeignItemId) -> &'hir ForeignItem<'hir> { + self.foreign_item(id) + } } trait Named { @@ -1021,7 +1046,7 @@ fn hir_id_to_string(map: &Map<'_>, id: HirId) -> String { ItemKind::Const(..) => "const", ItemKind::Fn(..) => "fn", ItemKind::Mod(..) => "mod", - ItemKind::ForeignMod(..) => "foreign mod", + ItemKind::ForeignMod { .. } => "foreign mod", ItemKind::GlobalAsm(..) => "global asm", ItemKind::TyAlias(..) => "ty", ItemKind::OpaqueTy(..) => "opaque type", diff --git a/compiler/rustc_middle/src/hir/place.rs b/compiler/rustc_middle/src/hir/place.rs index 5da4be4e982..143b3867d9f 100644 --- a/compiler/rustc_middle/src/hir/place.rs +++ b/compiler/rustc_middle/src/hir/place.rs @@ -4,7 +4,18 @@ use crate::ty::Ty; use rustc_hir::HirId; use rustc_target::abi::VariantIdx; -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)] +#[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + TyEncodable, + TyDecodable, + TypeFoldable, + HashStable +)] pub enum PlaceBase { /// A temporary variable Rvalue, @@ -16,7 +27,18 @@ pub enum PlaceBase { Upvar(ty::UpvarId), } -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)] +#[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + TyEncodable, + TyDecodable, + TypeFoldable, + HashStable +)] pub enum ProjectionKind { /// A dereference of a pointer, reference or `Box<T>` of the given type Deref, @@ -36,7 +58,18 @@ pub enum ProjectionKind { Subslice, } -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)] +#[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + TyEncodable, + TyDecodable, + TypeFoldable, + HashStable +)] pub struct Projection<'tcx> { /// Type after the projection is being applied. pub ty: Ty<'tcx>, @@ -48,7 +81,7 @@ pub struct Projection<'tcx> { /// A `Place` represents how a value is located in memory. /// /// This is an HIR version of `mir::Place` -#[derive(Clone, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, TypeFoldable, HashStable)] pub struct Place<'tcx> { /// The type of the `PlaceBase` pub base_ty: Ty<'tcx>, @@ -61,7 +94,7 @@ pub struct Place<'tcx> { /// A `PlaceWithHirId` represents how a value is located in memory. /// /// This is an HIR version of `mir::Place` -#[derive(Clone, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, TypeFoldable, HashStable)] pub struct PlaceWithHirId<'tcx> { /// `HirId` of the expression or pattern producing this value. pub hir_id: HirId, diff --git a/compiler/rustc_middle/src/middle/cstore.rs b/compiler/rustc_middle/src/middle/cstore.rs index ae9e4d364d3..6d2c43874bc 100644 --- a/compiler/rustc_middle/src/middle/cstore.rs +++ b/compiler/rustc_middle/src/middle/cstore.rs @@ -189,8 +189,12 @@ pub trait CrateStore { fn def_kind(&self, def: DefId) -> DefKind; fn def_path(&self, def: DefId) -> DefPath; fn def_path_hash(&self, def: DefId) -> DefPathHash; - fn all_def_path_hashes_and_def_ids(&self, cnum: CrateNum) -> Vec<(DefPathHash, DefId)>; - fn num_def_ids(&self, cnum: CrateNum) -> usize; + fn def_path_hash_to_def_id( + &self, + cnum: CrateNum, + index_guess: u32, + hash: DefPathHash, + ) -> Option<DefId>; // "queries" used in resolve that aren't tracked for incremental compilation fn crate_name_untracked(&self, cnum: CrateNum) -> Symbol; diff --git a/compiler/rustc_middle/src/middle/limits.rs b/compiler/rustc_middle/src/middle/limits.rs index 41342764ba7..61f850c2fc1 100644 --- a/compiler/rustc_middle/src/middle/limits.rs +++ b/compiler/rustc_middle/src/middle/limits.rs @@ -43,8 +43,7 @@ fn update_limit( let value_span = attr .meta() - .and_then(|meta| meta.name_value_literal().cloned()) - .map(|lit| lit.span) + .and_then(|meta| meta.name_value_literal_span()) .unwrap_or(attr.span); let error_str = match e.kind() { diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs index 978f08927c6..47c140e0b18 100644 --- a/compiler/rustc_middle/src/middle/stability.rs +++ b/compiler/rustc_middle/src/middle/stability.rs @@ -4,7 +4,7 @@ pub use self::StabilityLevel::*; use crate::ty::{self, TyCtxt}; -use rustc_ast::CRATE_NODE_ID; +use rustc_ast::NodeId; use rustc_attr::{self as attr, ConstStability, Deprecation, Stability}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::{Applicability, DiagnosticBuilder}; @@ -211,13 +211,14 @@ pub fn early_report_deprecation( suggestion: Option<Symbol>, lint: &'static Lint, span: Span, + node_id: NodeId, ) { if span.in_derive_expansion() { return; } let diag = BuiltinLintDiagnostics::DeprecatedMacro(suggestion, span); - lint_buffer.buffer_lint_with_diagnostic(lint, CRATE_NODE_ID, span, message, diag); + lint_buffer.buffer_lint_with_diagnostic(lint, node_id, span, message, diag); } fn late_report_deprecation( diff --git a/compiler/rustc_middle/src/mir/coverage.rs b/compiler/rustc_middle/src/mir/coverage.rs index 6b46d7c497d..8a6bf9dff7b 100644 --- a/compiler/rustc_middle/src/mir/coverage.rs +++ b/compiler/rustc_middle/src/mir/coverage.rs @@ -21,9 +21,9 @@ rustc_index::newtype_index! { impl ExpressionOperandId { /// An expression operand for a "zero counter", as described in the following references: /// - /// * <https://github.com/rust-lang/llvm-project/blob/llvmorg-8.0.0/llvm/docs/CoverageMappingFormat.rst#counter> - /// * <https://github.com/rust-lang/llvm-project/blob/llvmorg-8.0.0/llvm/docs/CoverageMappingFormat.rst#tag> - /// * <https://github.com/rust-lang/llvm-project/blob/llvmorg-8.0.0/llvm/docs/CoverageMappingFormat.rst#counter-expressions> + /// * <https://github.com/rust-lang/llvm-project/blob/rustc/11.0-2020-10-12/llvm/docs/CoverageMappingFormat.rst#counter> + /// * <https://github.com/rust-lang/llvm-project/blob/rustc/11.0-2020-10-12/llvm/docs/CoverageMappingFormat.rst#tag> + /// * <https://github.com/rust-lang/llvm-project/blob/rustc/11.0-2020-10-12/llvm/docs/CoverageMappingFormat.rst#counter-expressions> /// /// This operand can be used to count two or more separate code regions with a single counter, /// if they run sequentially with no branches, by injecting the `Counter` in a `BasicBlock` for diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 9289d4708de..814f91b0431 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -811,7 +811,7 @@ pub struct LocalDecl<'tcx> { /// after typeck. /// /// This should be sound because the drop flags are fully algebraic, and - /// therefore don't affect the OIBIT or outlives properties of the + /// therefore don't affect the auto-trait or outlives properties of the /// generator. pub internal: bool, diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index d8d639ab734..7538818b8af 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -1,70 +1,70 @@ +//! # The MIR Visitor +//! +//! ## Overview +//! +//! There are two visitors, one for immutable and one for mutable references, +//! but both are generated by the following macro. The code is written according +//! to the following conventions: +//! +//! - introduce a `visit_foo` and a `super_foo` method for every MIR type +//! - `visit_foo`, by default, calls `super_foo` +//! - `super_foo`, by default, destructures the `foo` and calls `visit_foo` +//! +//! This allows you as a user to override `visit_foo` for types are +//! interested in, and invoke (within that method) call +//! `self.super_foo` to get the default behavior. Just as in an OO +//! language, you should never call `super` methods ordinarily except +//! in that circumstance. +//! +//! For the most part, we do not destructure things external to the +//! MIR, e.g., types, spans, etc, but simply visit them and stop. This +//! avoids duplication with other visitors like `TypeFoldable`. +//! +//! ## Updating +//! +//! The code is written in a very deliberate style intended to minimize +//! the chance of things being overlooked. You'll notice that we always +//! use pattern matching to reference fields and we ensure that all +//! matches are exhaustive. +//! +//! For example, the `super_basic_block_data` method begins like this: +//! +//! ```rust +//! fn super_basic_block_data(&mut self, +//! block: BasicBlock, +//! data: & $($mutability)? BasicBlockData<'tcx>) { +//! let BasicBlockData { +//! statements, +//! terminator, +//! is_cleanup: _ +//! } = *data; +//! +//! for statement in statements { +//! self.visit_statement(block, statement); +//! } +//! +//! ... +//! } +//! ``` +//! +//! Here we used `let BasicBlockData { <fields> } = *data` deliberately, +//! rather than writing `data.statements` in the body. This is because if one +//! adds a new field to `BasicBlockData`, one will be forced to revise this code, +//! and hence one will (hopefully) invoke the correct visit methods (if any). +//! +//! For this to work, ALL MATCHES MUST BE EXHAUSTIVE IN FIELDS AND VARIANTS. +//! That means you never write `..` to skip over fields, nor do you write `_` +//! to skip over variants in a `match`. +//! +//! The only place that `_` is acceptable is to match a field (or +//! variant argument) that does not require visiting, as in +//! `is_cleanup` above. + use crate::mir::*; use crate::ty::subst::SubstsRef; use crate::ty::{CanonicalUserTypeAnnotation, Ty}; use rustc_span::Span; -// # The MIR Visitor -// -// ## Overview -// -// There are two visitors, one for immutable and one for mutable references, -// but both are generated by the following macro. The code is written according -// to the following conventions: -// -// - introduce a `visit_foo` and a `super_foo` method for every MIR type -// - `visit_foo`, by default, calls `super_foo` -// - `super_foo`, by default, destructures the `foo` and calls `visit_foo` -// -// This allows you as a user to override `visit_foo` for types are -// interested in, and invoke (within that method) call -// `self.super_foo` to get the default behavior. Just as in an OO -// language, you should never call `super` methods ordinarily except -// in that circumstance. -// -// For the most part, we do not destructure things external to the -// MIR, e.g., types, spans, etc, but simply visit them and stop. This -// avoids duplication with other visitors like `TypeFoldable`. -// -// ## Updating -// -// The code is written in a very deliberate style intended to minimize -// the chance of things being overlooked. You'll notice that we always -// use pattern matching to reference fields and we ensure that all -// matches are exhaustive. -// -// For example, the `super_basic_block_data` method begins like this: -// -// ```rust -// fn super_basic_block_data(&mut self, -// block: BasicBlock, -// data: & $($mutability)? BasicBlockData<'tcx>) { -// let BasicBlockData { -// statements, -// terminator, -// is_cleanup: _ -// } = *data; -// -// for statement in statements { -// self.visit_statement(block, statement); -// } -// -// ... -// } -// ``` -// -// Here we used `let BasicBlockData { <fields> } = *data` deliberately, -// rather than writing `data.statements` in the body. This is because if one -// adds a new field to `BasicBlockData`, one will be forced to revise this code, -// and hence one will (hopefully) invoke the correct visit methods (if any). -// -// For this to work, ALL MATCHES MUST BE EXHAUSTIVE IN FIELDS AND VARIANTS. -// That means you never write `..` to skip over fields, nor do you write `_` -// to skip over variants in a `match`. -// -// The only place that `_` is acceptable is to match a field (or -// variant argument) that does not require visiting, as in -// `is_cleanup` above. - macro_rules! make_mir_visitor { ($visitor_trait_name:ident, $($mutability:ident)?) => { pub trait $visitor_trait_name<'tcx> { @@ -254,7 +254,7 @@ macro_rules! make_mir_visitor { macro_rules! basic_blocks { (mut) => (body.basic_blocks_mut().iter_enumerated_mut()); () => (body.basic_blocks().iter_enumerated()); - }; + } for (bb, data) in basic_blocks!($($mutability)?) { self.visit_basic_block_data(bb, data); } @@ -275,7 +275,7 @@ macro_rules! make_mir_visitor { macro_rules! type_annotations { (mut) => (body.user_type_annotations.iter_enumerated_mut()); () => (body.user_type_annotations.iter_enumerated()); - }; + } for (index, annotation) in type_annotations!($($mutability)?) { self.visit_user_type_annotation( @@ -909,7 +909,7 @@ macro_rules! make_mir_visitor { macro_rules! basic_blocks { (mut) => (body.basic_blocks_mut()); () => (body.basic_blocks()); - }; + } let basic_block = & $($mutability)? basic_blocks!($($mutability)?)[location.block]; if basic_block.statements.len() == location.statement_index { if let Some(ref $($mutability)? terminator) = basic_block.terminator { @@ -1017,11 +1017,14 @@ macro_rules! visit_place_fns { let mut context = context; if !place.projection.is_empty() { - context = if context.is_mutating_use() { - PlaceContext::MutatingUse(MutatingUseContext::Projection) - } else { - PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection) - }; + if context.is_use() { + // ^ Only change the context if it is a real use, not a "use" in debuginfo. + context = if context.is_mutating_use() { + PlaceContext::MutatingUse(MutatingUseContext::Projection) + } else { + PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection) + }; + } } self.visit_local(&place.local, context, location); diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 634d50368bd..1b5f7a2c12e 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -130,8 +130,8 @@ rustc_queries! { storage(ArenaCacheSelector<'tcx>) cache_on_disk_if { key.is_local() } load_cached(tcx, id) { - let generics: Option<ty::Generics> = tcx.queries.on_disk_cache - .try_load_query_result(tcx, id); + let generics: Option<ty::Generics> = tcx.queries.on_disk_cache.as_ref() + .and_then(|c| c.try_load_query_result(tcx, id)); generics } } @@ -346,6 +346,21 @@ rustc_queries! { cache_on_disk_if { key.is_local() } } + /// Returns the name of the file that contains the function body, if instrumented for coverage. + query covered_file_name(key: DefId) -> Option<Symbol> { + desc { |tcx| "retrieving the covered file name, if instrumented, for `{}`", tcx.def_path_str(key) } + storage(ArenaCacheSelector<'tcx>) + cache_on_disk_if { key.is_local() } + } + + /// Returns the `CodeRegions` for a function that has instrumented coverage, in case the + /// function was optimized out before codegen, and before being added to the Coverage Map. + query covered_code_regions(key: DefId) -> Vec<&'tcx mir::coverage::CodeRegion> { + desc { |tcx| "retrieving the covered `CodeRegion`s, if instrumented, for `{}`", tcx.def_path_str(key) } + storage(ArenaCacheSelector<'tcx>) + cache_on_disk_if { key.is_local() } + } + /// The `DefId` is the `DefId` of the containing MIR body. Promoteds do not have their own /// `DefId`. This function returns all promoteds in the specified body. The body references /// promoteds by the `DefId` and the `mir::Promoted` index. This is necessary, because @@ -635,6 +650,10 @@ rustc_queries! { desc { |tcx| "checking loops in {}", describe_as_module(key, tcx) } } + query check_mod_naked_functions(key: LocalDefId) -> () { + desc { |tcx| "checking naked functions in {}", describe_as_module(key, tcx) } + } + query check_mod_item_types(key: LocalDefId) -> () { desc { |tcx| "checking item types in {}", describe_as_module(key, tcx) } } @@ -688,8 +707,8 @@ rustc_queries! { cache_on_disk_if { true } load_cached(tcx, id) { let typeck_results: Option<ty::TypeckResults<'tcx>> = tcx - .queries.on_disk_cache - .try_load_query_result(tcx, id); + .queries.on_disk_cache.as_ref() + .and_then(|c| c.try_load_query_result(tcx, id)); typeck_results.map(|x| &*tcx.arena.alloc(x)) } diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 4deb7225dcb..0a663f793aa 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -16,6 +16,7 @@ use crate::ty::{self, AdtKind, Ty, TyCtxt}; use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_hir as hir; use rustc_hir::def_id::DefId; +use rustc_hir::Constness; use rustc_span::symbol::Symbol; use rustc_span::{Span, DUMMY_SP}; use smallvec::SmallVec; @@ -70,7 +71,7 @@ pub enum Reveal { /// be observable directly by the user, `Reveal::All` /// should not be used by checks which may expose /// type equality or type contents to the user. - /// There are some exceptions, e.g., around OIBITS and + /// There are some exceptions, e.g., around auto traits and /// transmute-checking, which expose some details, but /// not the whole concrete type of the `impl Trait`. All, @@ -457,7 +458,7 @@ pub enum ImplSource<'tcx, N> { /// for some type parameter. The `Vec<N>` represents the /// obligations incurred from normalizing the where-clause (if /// any). - Param(Vec<N>), + Param(Vec<N>, Constness), /// Virtual calls through an object. Object(ImplSourceObjectData<'tcx, N>), @@ -487,7 +488,7 @@ impl<'tcx, N> ImplSource<'tcx, N> { pub fn nested_obligations(self) -> Vec<N> { match self { ImplSource::UserDefined(i) => i.nested, - ImplSource::Param(n) => n, + ImplSource::Param(n, _) => n, ImplSource::Builtin(i) => i.nested, ImplSource::AutoImpl(d) => d.nested, ImplSource::Closure(c) => c.nested, @@ -502,7 +503,7 @@ impl<'tcx, N> ImplSource<'tcx, N> { pub fn borrow_nested_obligations(&self) -> &[N] { match &self { ImplSource::UserDefined(i) => &i.nested[..], - ImplSource::Param(n) => &n[..], + ImplSource::Param(n, _) => &n[..], ImplSource::Builtin(i) => &i.nested[..], ImplSource::AutoImpl(d) => &d.nested[..], ImplSource::Closure(c) => &c.nested[..], @@ -524,7 +525,7 @@ impl<'tcx, N> ImplSource<'tcx, N> { substs: i.substs, nested: i.nested.into_iter().map(f).collect(), }), - ImplSource::Param(n) => ImplSource::Param(n.into_iter().map(f).collect()), + ImplSource::Param(n, ct) => ImplSource::Param(n.into_iter().map(f).collect(), ct), ImplSource::Builtin(i) => ImplSource::Builtin(ImplSourceBuiltinData { nested: i.nested.into_iter().map(f).collect(), }), diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs index c570ad3273d..e056240f941 100644 --- a/compiler/rustc_middle/src/traits/select.rs +++ b/compiler/rustc_middle/src/traits/select.rs @@ -101,7 +101,7 @@ pub enum SelectionCandidate<'tcx> { /// `false` if there are no *further* obligations. has_nested: bool, }, - ParamCandidate(ty::PolyTraitRef<'tcx>), + ParamCandidate(ty::ConstnessAnd<ty::PolyTraitRef<'tcx>>), ImplCandidate(DefId), AutoImplCandidate(DefId), diff --git a/compiler/rustc_middle/src/traits/structural_impls.rs b/compiler/rustc_middle/src/traits/structural_impls.rs index 194e275496e..5a17d38c734 100644 --- a/compiler/rustc_middle/src/traits/structural_impls.rs +++ b/compiler/rustc_middle/src/traits/structural_impls.rs @@ -21,7 +21,9 @@ impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSource<'tcx, N> { super::ImplSource::Object(ref d) => write!(f, "{:?}", d), - super::ImplSource::Param(ref n) => write!(f, "ImplSourceParamData({:?})", n), + super::ImplSource::Param(ref n, ct) => { + write!(f, "ImplSourceParamData({:?}, {:?})", n, ct) + } super::ImplSource::Builtin(ref d) => write!(f, "{:?}", d), diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs index 1def4936860..b2fc3710cd6 100644 --- a/compiler/rustc_middle/src/ty/codec.rs +++ b/compiler/rustc_middle/src/ty/codec.rs @@ -162,7 +162,8 @@ encodable_via_deref! { ty::Region<'tcx>, &'tcx mir::Body<'tcx>, &'tcx mir::UnsafetyCheckResult, - &'tcx mir::BorrowCheckResult<'tcx> + &'tcx mir::BorrowCheckResult<'tcx>, + &'tcx mir::coverage::CodeRegion } pub trait TyDecoder<'tcx>: Decoder { @@ -376,7 +377,8 @@ impl_decodable_via_ref! { &'tcx Allocation, &'tcx mir::Body<'tcx>, &'tcx mir::UnsafetyCheckResult, - &'tcx mir::BorrowCheckResult<'tcx> + &'tcx mir::BorrowCheckResult<'tcx>, + &'tcx mir::coverage::CodeRegion } #[macro_export] diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 36cbd36a770..a8d007c0be2 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -1,7 +1,7 @@ //! Type context book-keeping. use crate::arena::Arena; -use crate::dep_graph::{self, DepConstructor, DepGraph}; +use crate::dep_graph::{self, DepGraph, DepKind, DepNode, DepNodeExt}; use crate::hir::exports::ExportMap; use crate::ich::{NodeIdHashingMode, StableHashingContext}; use crate::infer::canonical::{Canonical, CanonicalVarInfo, CanonicalVarInfos}; @@ -34,15 +34,17 @@ use rustc_data_structures::stable_hasher::{ }; use rustc_data_structures::steal::Steal; use rustc_data_structures::sync::{self, Lock, Lrc, WorkerLocal}; -use rustc_data_structures::unhash::UnhashMap; use rustc_errors::ErrorReported; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LOCAL_CRATE}; -use rustc_hir::definitions::{DefPathHash, Definitions}; +use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId}; +use rustc_hir::def_id::{CRATE_DEF_INDEX, LOCAL_CRATE}; +use rustc_hir::definitions::Definitions; use rustc_hir::intravisit::Visitor; use rustc_hir::lang_items::LangItem; -use rustc_hir::{HirId, ItemKind, ItemLocalId, ItemLocalMap, ItemLocalSet, Node, TraitCandidate}; +use rustc_hir::{ + Constness, HirId, ItemKind, ItemLocalId, ItemLocalMap, ItemLocalSet, Node, TraitCandidate, +}; use rustc_index::vec::{Idx, IndexVec}; use rustc_macros::HashStable; use rustc_session::config::{BorrowckMode, CrateType, OutputFilenames}; @@ -622,6 +624,19 @@ impl<'tcx> TypeckResults<'tcx> { LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.pat_adjustments } } + /// For a given closure, returns the iterator of `ty::CapturedPlace`s that are captured + /// by the closure. + pub fn closure_min_captures_flattened( + &self, + closure_def_id: DefId, + ) -> impl Iterator<Item = &ty::CapturedPlace<'tcx>> { + self.closure_min_captures + .get(&closure_def_id) + .map(|closure_min_captures| closure_min_captures.values().flat_map(|v| v.iter())) + .into_iter() + .flatten() + } + pub fn upvar_capture(&self, upvar_id: ty::UpvarId) -> ty::UpvarCapture<'tcx> { self.upvar_capture_map[&upvar_id] } @@ -943,10 +958,6 @@ pub struct GlobalCtxt<'tcx> { pub(crate) untracked_crate: &'tcx hir::Crate<'tcx>, pub(crate) definitions: &'tcx Definitions, - /// A map from `DefPathHash` -> `DefId`. Includes `DefId`s from the local crate - /// as well as all upstream crates. Only populated in incremental mode. - pub def_path_hash_to_def_id: Option<UnhashMap<DefPathHash, DefId>>, - pub queries: query::Queries<'tcx>, maybe_unused_trait_imports: FxHashSet<LocalDefId>, @@ -1094,7 +1105,7 @@ impl<'tcx> TyCtxt<'tcx> { krate: &'tcx hir::Crate<'tcx>, definitions: &'tcx Definitions, dep_graph: DepGraph, - on_disk_query_result_cache: query::OnDiskCache<'tcx>, + on_disk_query_result_cache: Option<query::OnDiskCache<'tcx>>, crate_name: &str, output_filenames: &OutputFilenames, ) -> GlobalCtxt<'tcx> { @@ -1111,21 +1122,6 @@ impl<'tcx> TyCtxt<'tcx> { let mut providers = IndexVec::from_elem_n(extern_providers, max_cnum + 1); providers[LOCAL_CRATE] = local_providers; - let def_path_hash_to_def_id = if s.opts.build_dep_graph() { - let capacity = definitions.def_path_table().num_def_ids() - + crates.iter().map(|cnum| cstore.num_def_ids(*cnum)).sum::<usize>(); - let mut map = UnhashMap::with_capacity_and_hasher(capacity, Default::default()); - - map.extend(definitions.def_path_table().all_def_path_hashes_and_def_ids(LOCAL_CRATE)); - for cnum in &crates { - map.extend(cstore.all_def_path_hashes_and_def_ids(*cnum).into_iter()); - } - - Some(map) - } else { - None - }; - let mut trait_map: FxHashMap<_, FxHashMap<_, _>> = FxHashMap::default(); for (hir_id, v) in krate.trait_map.iter() { let map = trait_map.entry(hir_id.owner).or_default(); @@ -1153,7 +1149,6 @@ impl<'tcx> TyCtxt<'tcx> { extern_prelude: resolutions.extern_prelude, untracked_crate: krate, definitions, - def_path_hash_to_def_id, queries: query::Queries::new(providers, extern_providers, on_disk_query_result_cache), ty_rcache: Default::default(), pred_rcache: Default::default(), @@ -1327,7 +1322,8 @@ impl<'tcx> TyCtxt<'tcx> { // We cannot use the query versions of crates() and crate_hash(), since // those would need the DepNodes that we are allocating here. for cnum in self.cstore.crates_untracked() { - let dep_node = DepConstructor::CrateMetadata(self, cnum); + let def_path_hash = self.def_path_hash(DefId { krate: cnum, index: CRATE_DEF_INDEX }); + let dep_node = DepNode::from_def_path_hash(def_path_hash, DepKind::CrateMetadata); let crate_hash = self.cstore.crate_hash_untracked(cnum); self.dep_graph.with_task( dep_node, @@ -1343,7 +1339,7 @@ impl<'tcx> TyCtxt<'tcx> { where E: ty::codec::OpaqueEncoder, { - self.queries.on_disk_cache.serialize(self, encoder) + self.queries.on_disk_cache.as_ref().map(|c| c.serialize(self, encoder)).unwrap_or(Ok(())) } /// If `true`, we should use the MIR-based borrowck, but also @@ -1635,6 +1631,8 @@ nop_list_lift! {projs; ProjectionKind => ProjectionKind} // This is the impl for `&'a InternalSubsts<'a>`. nop_list_lift! {substs; GenericArg<'a> => GenericArg<'tcx>} +CloneLiftImpls! { for<'tcx> { Constness, } } + pub mod tls { use super::{ptr_eq, GlobalCtxt, TyCtxt}; diff --git a/compiler/rustc_middle/src/ty/fold.rs b/compiler/rustc_middle/src/ty/fold.rs index 1883c89a151..13c8d6b2bcc 100644 --- a/compiler/rustc_middle/src/ty/fold.rs +++ b/compiler/rustc_middle/src/ty/fold.rs @@ -581,7 +581,7 @@ impl<'tcx> TyCtxt<'tcx> { let mut const_map = FxHashMap::default(); if !value.has_escaping_bound_vars() { - (value.clone(), region_map) + (value, region_map) } else { let mut real_fld_r = |br| *region_map.entry(br).or_insert_with(|| fld_r(br)); diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index f52466d85f8..413c9cca589 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -540,7 +540,7 @@ fn polymorphize<'tcx>( struct PolymorphizationFolder<'tcx> { tcx: TyCtxt<'tcx>, - }; + } impl ty::TypeFolder<'tcx> for PolymorphizationFolder<'tcx> { fn tcx<'a>(&'a self) -> TyCtxt<'tcx> { diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index acc7d3c4960..d6b3afb3be3 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -15,7 +15,7 @@ use rustc_session::{DataTypeKind, FieldInfo, SizeKind, VariantInfo}; use rustc_span::symbol::{Ident, Symbol}; use rustc_span::DUMMY_SP; use rustc_target::abi::call::{ - ArgAbi, ArgAttribute, ArgAttributes, Conv, FnAbi, PassMode, Reg, RegKind, + ArgAbi, ArgAttribute, ArgAttributes, ArgExtension, Conv, FnAbi, PassMode, Reg, RegKind, }; use rustc_target::abi::*; use rustc_target::spec::{abi::Abi as SpecAbi, HasTargetSpec, PanicStrategy}; @@ -176,7 +176,7 @@ impl<'tcx> fmt::Display for LayoutError<'tcx> { match *self { LayoutError::Unknown(ty) => write!(f, "the type `{}` has an unknown layout", ty), LayoutError::SizeOverflow(ty) => { - write!(f, "the type `{}` is too big for the current architecture", ty) + write!(f, "values of the type `{}` are too big for the current architecture", ty) } } } @@ -631,30 +631,106 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { } // SIMD vector types. - ty::Adt(def, ..) if def.repr.simd() => { - let element = self.layout_of(ty.simd_type(tcx))?; - let count = ty.simd_size(tcx); - assert!(count > 0); - let scalar = match element.abi { - Abi::Scalar(ref scalar) => scalar.clone(), - _ => { + ty::Adt(def, substs) if def.repr.simd() => { + // Supported SIMD vectors are homogeneous ADTs with at least one field: + // + // * #[repr(simd)] struct S(T, T, T, T); + // * #[repr(simd)] struct S { x: T, y: T, z: T, w: T } + // * #[repr(simd)] struct S([T; 4]) + // + // where T is a primitive scalar (integer/float/pointer). + + // SIMD vectors with zero fields are not supported. + // (should be caught by typeck) + if def.non_enum_variant().fields.is_empty() { + tcx.sess.fatal(&format!("monomorphising SIMD type `{}` of zero length", ty)); + } + + // Type of the first ADT field: + let f0_ty = def.non_enum_variant().fields[0].ty(tcx, substs); + + // Heterogeneous SIMD vectors are not supported: + // (should be caught by typeck) + for fi in &def.non_enum_variant().fields { + if fi.ty(tcx, substs) != f0_ty { + tcx.sess.fatal(&format!("monomorphising heterogeneous SIMD type `{}`", ty)); + } + } + + // The element type and number of elements of the SIMD vector + // are obtained from: + // + // * the element type and length of the single array field, if + // the first field is of array type, or + // + // * the homogenous field type and the number of fields. + let (e_ty, e_len, is_array) = if let ty::Array(e_ty, _) = f0_ty.kind() { + // First ADT field is an array: + + // SIMD vectors with multiple array fields are not supported: + // (should be caught by typeck) + if def.non_enum_variant().fields.len() != 1 { tcx.sess.fatal(&format!( - "monomorphising SIMD type `{}` with \ - a non-machine element type `{}`", - ty, element.ty + "monomorphising SIMD type `{}` with more than one array field", + ty )); } + + // Extract the number of elements from the layout of the array field: + let len = if let Ok(TyAndLayout { + layout: Layout { fields: FieldsShape::Array { count, .. }, .. }, + .. + }) = self.layout_of(f0_ty) + { + count + } else { + return Err(LayoutError::Unknown(ty)); + }; + + (*e_ty, *len, true) + } else { + // First ADT field is not an array: + (f0_ty, def.non_enum_variant().fields.len() as _, false) }; - let size = - element.size.checked_mul(count, dl).ok_or(LayoutError::SizeOverflow(ty))?; + + // SIMD vectors of zero length are not supported. + // + // Can't be caught in typeck if the array length is generic. + if e_len == 0 { + tcx.sess.fatal(&format!("monomorphising SIMD type `{}` of zero length", ty)); + } + + // Compute the ABI of the element type: + let e_ly = self.layout_of(e_ty)?; + let e_abi = if let Abi::Scalar(ref scalar) = e_ly.abi { + scalar.clone() + } else { + // This error isn't caught in typeck, e.g., if + // the element type of the vector is generic. + tcx.sess.fatal(&format!( + "monomorphising SIMD type `{}` with a non-primitive-scalar \ + (integer/float/pointer) element type `{}`", + ty, e_ty + )) + }; + + // Compute the size and alignment of the vector: + let size = e_ly.size.checked_mul(e_len, dl).ok_or(LayoutError::SizeOverflow(ty))?; let align = dl.vector_align(size); let size = size.align_to(align.abi); + // Compute the placement of the vector fields: + let fields = if is_array { + FieldsShape::Arbitrary { offsets: vec![Size::ZERO], memory_index: vec![0] } + } else { + FieldsShape::Array { stride: e_ly.size, count: e_len } + }; + tcx.intern_layout(Layout { variants: Variants::Single { index: VariantIdx::new(0) }, - fields: FieldsShape::Array { stride: element.size, count }, - abi: Abi::Vector { element: scalar, count }, - largest_niche: element.largest_niche.clone(), + fields, + abi: Abi::Vector { element: e_abi, count: e_len }, + largest_niche: e_ly.largest_niche.clone(), size, align, }) @@ -2029,121 +2105,148 @@ where } fn field(this: TyAndLayout<'tcx>, cx: &C, i: usize) -> C::TyAndLayout { - let tcx = cx.tcx(); - let tag_layout = |tag: &Scalar| -> C::TyAndLayout { - let layout = Layout::scalar(cx, tag.clone()); - MaybeResult::from(Ok(TyAndLayout { - layout: tcx.intern_layout(layout), - ty: tag.value.to_ty(tcx), - })) - }; + enum TyMaybeWithLayout<C: LayoutOf> { + Ty(C::Ty), + TyAndLayout(C::TyAndLayout), + } - cx.layout_of(match *this.ty.kind() { - ty::Bool - | ty::Char - | ty::Int(_) - | ty::Uint(_) - | ty::Float(_) - | ty::FnPtr(_) - | ty::Never - | ty::FnDef(..) - | ty::GeneratorWitness(..) - | ty::Foreign(..) - | ty::Dynamic(..) => bug!("TyAndLayout::field_type({:?}): not applicable", this), - - // Potentially-fat pointers. - ty::Ref(_, pointee, _) | ty::RawPtr(ty::TypeAndMut { ty: pointee, .. }) => { - assert!(i < this.fields.count()); - - // Reuse the fat `*T` type as its own thin pointer data field. - // This provides information about, e.g., DST struct pointees - // (which may have no non-DST form), and will work as long - // as the `Abi` or `FieldsShape` is checked by users. - if i == 0 { - let nil = tcx.mk_unit(); - let ptr_ty = if this.ty.is_unsafe_ptr() { - tcx.mk_mut_ptr(nil) - } else { - tcx.mk_mut_ref(tcx.lifetimes.re_static, nil) - }; - return MaybeResult::from(cx.layout_of(ptr_ty).to_result().map( - |mut ptr_layout| { - ptr_layout.ty = this.ty; - ptr_layout - }, - )); - } + fn ty_and_layout_kind< + C: LayoutOf<Ty = Ty<'tcx>, TyAndLayout: MaybeResult<TyAndLayout<'tcx>>> + + HasTyCtxt<'tcx> + + HasParamEnv<'tcx>, + >( + this: TyAndLayout<'tcx>, + cx: &C, + i: usize, + ty: C::Ty, + ) -> TyMaybeWithLayout<C> { + let tcx = cx.tcx(); + let tag_layout = |tag: &Scalar| -> C::TyAndLayout { + let layout = Layout::scalar(cx, tag.clone()); + MaybeResult::from(Ok(TyAndLayout { + layout: tcx.intern_layout(layout), + ty: tag.value.to_ty(tcx), + })) + }; - match tcx.struct_tail_erasing_lifetimes(pointee, cx.param_env()).kind() { - ty::Slice(_) | ty::Str => tcx.types.usize, - ty::Dynamic(_, _) => { - tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.mk_array(tcx.types.usize, 3)) - /* FIXME: use actual fn pointers - Warning: naively computing the number of entries in the - vtable by counting the methods on the trait + methods on - all parent traits does not work, because some methods can - be not object safe and thus excluded from the vtable. - Increase this counter if you tried to implement this but - failed to do it without duplicating a lot of code from - other places in the compiler: 2 - tcx.mk_tup(&[ - tcx.mk_array(tcx.types.usize, 3), - tcx.mk_array(Option<fn()>), - ]) - */ + match *ty.kind() { + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::FnPtr(_) + | ty::Never + | ty::FnDef(..) + | ty::GeneratorWitness(..) + | ty::Foreign(..) + | ty::Dynamic(..) => bug!("TyAndLayout::field_type({:?}): not applicable", this), + + // Potentially-fat pointers. + ty::Ref(_, pointee, _) | ty::RawPtr(ty::TypeAndMut { ty: pointee, .. }) => { + assert!(i < this.fields.count()); + + // Reuse the fat `*T` type as its own thin pointer data field. + // This provides information about, e.g., DST struct pointees + // (which may have no non-DST form), and will work as long + // as the `Abi` or `FieldsShape` is checked by users. + if i == 0 { + let nil = tcx.mk_unit(); + let ptr_ty = if ty.is_unsafe_ptr() { + tcx.mk_mut_ptr(nil) + } else { + tcx.mk_mut_ref(tcx.lifetimes.re_static, nil) + }; + return TyMaybeWithLayout::TyAndLayout(MaybeResult::from( + cx.layout_of(ptr_ty).to_result().map(|mut ptr_layout| { + ptr_layout.ty = ty; + ptr_layout + }), + )); } - _ => bug!("TyAndLayout::field_type({:?}): not applicable", this), - } - } - // Arrays and slices. - ty::Array(element, _) | ty::Slice(element) => element, - ty::Str => tcx.types.u8, - - // Tuples, generators and closures. - ty::Closure(_, ref substs) => substs.as_closure().upvar_tys().nth(i).unwrap(), - - ty::Generator(def_id, ref substs, _) => match this.variants { - Variants::Single { index } => substs - .as_generator() - .state_tys(def_id, tcx) - .nth(index.as_usize()) - .unwrap() - .nth(i) - .unwrap(), - Variants::Multiple { ref tag, tag_field, .. } => { - if i == tag_field { - return tag_layout(tag); + match tcx.struct_tail_erasing_lifetimes(pointee, cx.param_env()).kind() { + ty::Slice(_) | ty::Str => TyMaybeWithLayout::Ty(tcx.types.usize), + ty::Dynamic(_, _) => { + TyMaybeWithLayout::Ty(tcx.mk_imm_ref( + tcx.lifetimes.re_static, + tcx.mk_array(tcx.types.usize, 3), + )) + /* FIXME: use actual fn pointers + Warning: naively computing the number of entries in the + vtable by counting the methods on the trait + methods on + all parent traits does not work, because some methods can + be not object safe and thus excluded from the vtable. + Increase this counter if you tried to implement this but + failed to do it without duplicating a lot of code from + other places in the compiler: 2 + tcx.mk_tup(&[ + tcx.mk_array(tcx.types.usize, 3), + tcx.mk_array(Option<fn()>), + ]) + */ + } + _ => bug!("TyAndLayout::field_type({:?}): not applicable", this), } - substs.as_generator().prefix_tys().nth(i).unwrap() } - }, - ty::Tuple(tys) => tys[i].expect_ty(), + // Arrays and slices. + ty::Array(element, _) | ty::Slice(element) => TyMaybeWithLayout::Ty(element), + ty::Str => TyMaybeWithLayout::Ty(tcx.types.u8), - // SIMD vector types. - ty::Adt(def, ..) if def.repr.simd() => this.ty.simd_type(tcx), + // Tuples, generators and closures. + ty::Closure(_, ref substs) => { + ty_and_layout_kind(this, cx, i, substs.as_closure().tupled_upvars_ty()) + } - // ADTs. - ty::Adt(def, substs) => { - match this.variants { - Variants::Single { index } => def.variants[index].fields[i].ty(tcx, substs), + ty::Generator(def_id, ref substs, _) => match this.variants { + Variants::Single { index } => TyMaybeWithLayout::Ty( + substs + .as_generator() + .state_tys(def_id, tcx) + .nth(index.as_usize()) + .unwrap() + .nth(i) + .unwrap(), + ), + Variants::Multiple { ref tag, tag_field, .. } => { + if i == tag_field { + return TyMaybeWithLayout::TyAndLayout(tag_layout(tag)); + } + TyMaybeWithLayout::Ty(substs.as_generator().prefix_tys().nth(i).unwrap()) + } + }, + + ty::Tuple(tys) => TyMaybeWithLayout::Ty(tys[i].expect_ty()), - // Discriminant field for enums (where applicable). - Variants::Multiple { ref tag, .. } => { - assert_eq!(i, 0); - return tag_layout(tag); + // ADTs. + ty::Adt(def, substs) => { + match this.variants { + Variants::Single { index } => { + TyMaybeWithLayout::Ty(def.variants[index].fields[i].ty(tcx, substs)) + } + + // Discriminant field for enums (where applicable). + Variants::Multiple { ref tag, .. } => { + assert_eq!(i, 0); + return TyMaybeWithLayout::TyAndLayout(tag_layout(tag)); + } } } + + ty::Projection(_) + | ty::Bound(..) + | ty::Placeholder(..) + | ty::Opaque(..) + | ty::Param(_) + | ty::Infer(_) + | ty::Error(_) => bug!("TyAndLayout::field_type: unexpected type `{}`", this.ty), } + } - ty::Projection(_) - | ty::Bound(..) - | ty::Placeholder(..) - | ty::Opaque(..) - | ty::Param(_) - | ty::Infer(_) - | ty::Error(_) => bug!("TyAndLayout::field_type: unexpected type `{}`", this.ty), + cx.layout_of(match ty_and_layout_kind(this, cx, i, this.ty) { + TyMaybeWithLayout::Ty(result) => result, + TyMaybeWithLayout::TyAndLayout(result) => return result, }) } @@ -2619,7 +2722,7 @@ where is_return: bool| { // Booleans are always an i1 that needs to be zero-extended. if scalar.is_bool() { - attrs.set(ArgAttribute::ZExt); + attrs.ext(ArgExtension::Zext); return; } @@ -2745,7 +2848,7 @@ where || abi == SpecAbi::RustIntrinsic || abi == SpecAbi::PlatformIntrinsic { - let fixup = |arg: &mut ArgAbi<'tcx, Ty<'tcx>>, is_ret: bool| { + let fixup = |arg: &mut ArgAbi<'tcx, Ty<'tcx>>| { if arg.is_ignore() { return; } @@ -2783,9 +2886,9 @@ where _ => return, } - // Return structures up to 2 pointers in size by value, matching `ScalarPair`. LLVM - // will usually return these in 2 registers, which is more efficient than by-ref. - let max_by_val_size = if is_ret { Pointer.size(cx) * 2 } else { Pointer.size(cx) }; + // Pass and return structures up to 2 pointers in size by value, matching `ScalarPair`. + // LLVM will usually pass these in 2 registers, which is more efficient than by-ref. + let max_by_val_size = Pointer.size(cx) * 2; let size = arg.layout.size; if arg.layout.is_unsized() || size > max_by_val_size { @@ -2797,12 +2900,9 @@ where arg.cast_to(Reg { kind: RegKind::Integer, size }); } }; - fixup(&mut self.ret, true); + fixup(&mut self.ret); for arg in &mut self.args { - fixup(arg, false); - } - if let PassMode::Indirect(ref mut attrs, _) = self.ret.mode { - attrs.set(ArgAttribute::StructRet); + fixup(arg); } return; } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 6a67935cd98..7428f34153c 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -56,7 +56,7 @@ pub use self::sty::InferTy::*; pub use self::sty::RegionKind; pub use self::sty::RegionKind::*; pub use self::sty::TyKind::*; -pub use self::sty::{Binder, BoundTy, BoundTyKind, BoundVar, DebruijnIndex, INNERMOST}; +pub use self::sty::{Binder, BoundTy, BoundTyKind, BoundVar}; pub use self::sty::{BoundRegion, EarlyBoundRegion, FreeRegion, Region}; pub use self::sty::{CanonicalPolyFnSig, FnSig, GenSig, PolyFnSig, PolyGenSig}; pub use self::sty::{ClosureSubsts, GeneratorSubsts, TypeAndMut, UpvarSubsts}; @@ -67,6 +67,7 @@ pub use self::sty::{ExistentialProjection, PolyExistentialProjection}; pub use self::sty::{ExistentialTraitRef, PolyExistentialTraitRef}; pub use self::sty::{PolyTraitRef, TraitRef, TyKind}; pub use crate::ty::diagnostics::*; +pub use rustc_type_ir::{DebruijnIndex, TypeFlags, INNERMOST}; pub use self::binding::BindingMode; pub use self::binding::BindingMode::*; @@ -497,91 +498,6 @@ pub struct CReaderCacheKey { pub pos: usize, } -bitflags! { - /// Flags that we track on types. These flags are propagated upwards - /// through the type during type construction, so that we can quickly check - /// whether the type has various kinds of types in it without recursing - /// over the type itself. - pub struct TypeFlags: u32 { - // Does this have parameters? Used to determine whether substitution is - // required. - /// Does this have [Param]? - const HAS_TY_PARAM = 1 << 0; - /// Does this have [ReEarlyBound]? - const HAS_RE_PARAM = 1 << 1; - /// Does this have [ConstKind::Param]? - const HAS_CT_PARAM = 1 << 2; - - const NEEDS_SUBST = TypeFlags::HAS_TY_PARAM.bits - | TypeFlags::HAS_RE_PARAM.bits - | TypeFlags::HAS_CT_PARAM.bits; - - /// Does this have [Infer]? - const HAS_TY_INFER = 1 << 3; - /// Does this have [ReVar]? - const HAS_RE_INFER = 1 << 4; - /// Does this have [ConstKind::Infer]? - const HAS_CT_INFER = 1 << 5; - - /// Does this have inference variables? Used to determine whether - /// inference is required. - const NEEDS_INFER = TypeFlags::HAS_TY_INFER.bits - | TypeFlags::HAS_RE_INFER.bits - | TypeFlags::HAS_CT_INFER.bits; - - /// Does this have [Placeholder]? - const HAS_TY_PLACEHOLDER = 1 << 6; - /// Does this have [RePlaceholder]? - const HAS_RE_PLACEHOLDER = 1 << 7; - /// Does this have [ConstKind::Placeholder]? - const HAS_CT_PLACEHOLDER = 1 << 8; - - /// `true` if there are "names" of regions and so forth - /// that are local to a particular fn/inferctxt - const HAS_FREE_LOCAL_REGIONS = 1 << 9; - - /// `true` if there are "names" of types and regions and so forth - /// that are local to a particular fn - const HAS_FREE_LOCAL_NAMES = TypeFlags::HAS_TY_PARAM.bits - | TypeFlags::HAS_CT_PARAM.bits - | TypeFlags::HAS_TY_INFER.bits - | TypeFlags::HAS_CT_INFER.bits - | TypeFlags::HAS_TY_PLACEHOLDER.bits - | TypeFlags::HAS_CT_PLACEHOLDER.bits - | TypeFlags::HAS_FREE_LOCAL_REGIONS.bits; - - /// Does this have [Projection]? - const HAS_TY_PROJECTION = 1 << 10; - /// Does this have [Opaque]? - const HAS_TY_OPAQUE = 1 << 11; - /// Does this have [ConstKind::Unevaluated]? - const HAS_CT_PROJECTION = 1 << 12; - - /// Could this type be normalized further? - const HAS_PROJECTION = TypeFlags::HAS_TY_PROJECTION.bits - | TypeFlags::HAS_TY_OPAQUE.bits - | TypeFlags::HAS_CT_PROJECTION.bits; - - /// Is an error type/const reachable? - const HAS_ERROR = 1 << 13; - - /// Does this have any region that "appears free" in the type? - /// Basically anything but [ReLateBound] and [ReErased]. - const HAS_FREE_REGIONS = 1 << 14; - - /// Does this have any [ReLateBound] regions? Used to check - /// if a global bound is safe to evaluate. - const HAS_RE_LATE_BOUND = 1 << 15; - - /// Does this have any [ReErased] regions? - const HAS_RE_ERASED = 1 << 16; - - /// Does this value have parameters/placeholders/inference variables which could be - /// replaced later, in a way that would change the results of `impl` specialization? - const STILL_FURTHER_SPECIALIZABLE = 1 << 17; - } -} - #[allow(rustc::usage_of_ty_tykind)] pub struct TyS<'tcx> { /// This field shouldn't be used directly and may be removed in the future. @@ -672,7 +588,18 @@ impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for TyS<'tcx> { #[rustc_diagnostic_item = "Ty"] pub type Ty<'tcx> = &'tcx TyS<'tcx>; -#[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)] +#[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + TyEncodable, + TyDecodable, + TypeFoldable, + HashStable +)] pub struct UpvarPath { pub hir_id: hir::HirId, } @@ -680,7 +607,7 @@ pub struct UpvarPath { /// Upvars do not get their own `NodeId`. Instead, we use the pair of /// the original var ID (that is, the root variable that is referenced /// by the upvar) and the ID of the closure expression. -#[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable, TypeFoldable, HashStable)] pub struct UpvarId { pub var_path: UpvarPath, pub closure_expr_id: LocalDefId, @@ -692,7 +619,7 @@ impl UpvarId { } } -#[derive(Clone, PartialEq, Debug, TyEncodable, TyDecodable, Copy, HashStable)] +#[derive(Clone, PartialEq, Debug, TyEncodable, TyDecodable, TypeFoldable, Copy, HashStable)] pub enum BorrowKind { /// Data must be immutable and is aliasable. ImmBorrow, @@ -746,7 +673,7 @@ pub enum BorrowKind { /// Information describing the capture of an upvar. This is computed /// during `typeck`, specifically by `regionck`. -#[derive(PartialEq, Clone, Debug, Copy, TyEncodable, TyDecodable, HashStable)] +#[derive(PartialEq, Clone, Debug, Copy, TyEncodable, TyDecodable, TypeFoldable, HashStable)] pub enum UpvarCapture<'tcx> { /// Upvar is captured by value. This is always true when the /// closure is labeled `move`, but can also be true in other cases @@ -763,7 +690,7 @@ pub enum UpvarCapture<'tcx> { ByRef(UpvarBorrow<'tcx>), } -#[derive(PartialEq, Clone, Copy, TyEncodable, TyDecodable, HashStable)] +#[derive(PartialEq, Clone, Copy, TyEncodable, TyDecodable, TypeFoldable, HashStable)] pub struct UpvarBorrow<'tcx> { /// The kind of borrow: by-ref upvars have access to shared /// immutable borrows, which are not part of the normal language @@ -790,7 +717,7 @@ pub type RootVariableMinCaptureList<'tcx> = FxIndexMap<hir::HirId, MinCaptureLis pub type MinCaptureList<'tcx> = Vec<CapturedPlace<'tcx>>; /// A `Place` and the corresponding `CaptureInfo`. -#[derive(PartialEq, Clone, Debug, TyEncodable, TyDecodable, HashStable)] +#[derive(PartialEq, Clone, Debug, TyEncodable, TyDecodable, TypeFoldable, HashStable)] pub struct CapturedPlace<'tcx> { pub place: HirPlace<'tcx>, pub info: CaptureInfo<'tcx>, @@ -799,7 +726,7 @@ pub struct CapturedPlace<'tcx> { /// Part of `MinCaptureInformationMap`; describes the capture kind (&, &mut, move) /// for a particular capture as well as identifying the part of the source code /// that triggered this capture to occur. -#[derive(PartialEq, Clone, Debug, Copy, TyEncodable, TyDecodable, HashStable)] +#[derive(PartialEq, Clone, Debug, Copy, TyEncodable, TyDecodable, TypeFoldable, HashStable)] pub struct CaptureInfo<'tcx> { /// Expr Id pointing to use that resulted in selecting the current capture kind /// @@ -1503,9 +1430,11 @@ impl<'tcx> ToPredicate<'tcx> for PolyProjectionPredicate<'tcx> { } impl<'tcx> Predicate<'tcx> { - pub fn to_opt_poly_trait_ref(self) -> Option<PolyTraitRef<'tcx>> { + pub fn to_opt_poly_trait_ref(self) -> Option<ConstnessAnd<PolyTraitRef<'tcx>>> { match self.skip_binders() { - PredicateAtom::Trait(t, _) => Some(ty::Binder::bind(t.trait_ref)), + PredicateAtom::Trait(t, constness) => { + Some(ConstnessAnd { constness, value: ty::Binder::bind(t.trait_ref) }) + } PredicateAtom::Projection(..) | PredicateAtom::Subtype(..) | PredicateAtom::RegionOutlives(..) @@ -1681,34 +1610,59 @@ pub struct BoundConst<'tcx> { pub type PlaceholderConst<'tcx> = Placeholder<BoundConst<'tcx>>; -/// A `DefId` which is potentially bundled with its corresponding generic parameter -/// in case `did` is a const argument. -/// -/// This is used to prevent cycle errors during typeck -/// as `type_of(const_arg)` depends on `typeck(owning_body)` -/// which once again requires the type of its generic arguments. +/// A `DefId` which, in case it is a const argument, is potentially bundled with +/// the `DefId` of the generic parameter it instantiates. /// -/// Luckily we only need to deal with const arguments once we -/// know their corresponding parameters. We (ab)use this by -/// calling `type_of(param_did)` for these arguments. +/// This is used to avoid calls to `type_of` for const arguments during typeck +/// which cause cycle errors. /// /// ```rust /// #![feature(const_generics)] /// /// struct A; /// impl A { -/// fn foo<const N: usize>(&self) -> usize { N } +/// fn foo<const N: usize>(&self) -> [u8; N] { [0; N] } +/// // ^ const parameter /// } /// struct B; /// impl B { -/// fn foo<const N: u8>(&self) -> usize { 42 } +/// fn foo<const M: u8>(&self) -> usize { 42 } +/// // ^ const parameter /// } /// /// fn main() { /// let a = A; -/// a.foo::<7>(); +/// let _b = a.foo::<{ 3 + 7 }>(); +/// // ^^^^^^^^^ const argument /// } /// ``` +/// +/// Let's look at the call `a.foo::<{ 3 + 7 }>()` here. We do not know +/// which `foo` is used until we know the type of `a`. +/// +/// We only know the type of `a` once we are inside of `typeck(main)`. +/// We also end up normalizing the type of `_b` during `typeck(main)` which +/// requires us to evaluate the const argument. +/// +/// To evaluate that const argument we need to know its type, +/// which we would get using `type_of(const_arg)`. This requires us to +/// resolve `foo` as it can be either `usize` or `u8` in this example. +/// However, resolving `foo` once again requires `typeck(main)` to get the type of `a`, +/// which results in a cycle. +/// +/// In short we must not call `type_of(const_arg)` during `typeck(main)`. +/// +/// When first creating the `ty::Const` of the const argument inside of `typeck` we have +/// already resolved `foo` so we know which const parameter this argument instantiates. +/// This means that we also know the expected result of `type_of(const_arg)` even if we +/// aren't allowed to call that query: it is equal to `type_of(const_param)` which is +/// trivial to compute. +/// +/// If we now want to use that constant in a place which potentionally needs its type +/// we also pass the type of its `const_param`. This is the point of `WithOptConstParam`, +/// except that instead of a `Ty` we bundle the `DefId` of the const parameter. +/// Meaning that we need to use `type_of(const_param_did)` if `const_param_did` is `Some` +/// to get the type of `did`. #[derive(Copy, Clone, Debug, TypeFoldable, Lift, TyEncodable, TyDecodable)] #[derive(PartialEq, Eq, PartialOrd, Ord)] #[derive(Hash, HashStable)] @@ -1719,7 +1673,7 @@ pub struct WithOptConstParam<T> { /// /// Note that even if `did` is a const argument, this may still be `None`. /// All queries taking `WithOptConstParam` start by calling `tcx.opt_const_param_of(def.did)` - /// to potentially update `param_did` in case it `None`. + /// to potentially update `param_did` in the case it is `None`. pub const_param_did: Option<DefId>, } @@ -1947,7 +1901,7 @@ impl<'tcx> ParamEnv<'tcx> { } } -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, TypeFoldable)] pub struct ConstnessAnd<T> { pub constness: Constness, pub value: T, diff --git a/compiler/rustc_middle/src/ty/query/mod.rs b/compiler/rustc_middle/src/ty/query/mod.rs index 187f86a52f4..b269dd09b72 100644 --- a/compiler/rustc_middle/src/ty/query/mod.rs +++ b/compiler/rustc_middle/src/ty/query/mod.rs @@ -220,7 +220,7 @@ pub(crate) fn try_load_from_on_disk_cache<'tcx>(tcx: TyCtxt<'tcx>, dep_node: &De .map(|c| c.is_green()) .unwrap_or(false)); - let key = <query_keys::$name<'tcx> as DepNodeParams<TyCtxt<'_>>>::recover(tcx, dep_node).unwrap(); + let key = <query_keys::$name<'tcx> as DepNodeParams<TyCtxt<'_>>>::recover(tcx, dep_node).unwrap_or_else(|| panic!("Failed to recover key for {:?} with hash {}", dep_node, dep_node.hash)); if queries::$name::cache_on_disk(tcx, &key, None) { let _ = tcx.$name(key); } diff --git a/compiler/rustc_middle/src/ty/query/on_disk_cache.rs b/compiler/rustc_middle/src/ty/query/on_disk_cache.rs index 173e9a31928..3eed94b1ffb 100644 --- a/compiler/rustc_middle/src/ty/query/on_disk_cache.rs +++ b/compiler/rustc_middle/src/ty/query/on_disk_cache.rs @@ -11,6 +11,7 @@ use rustc_data_structures::thin_vec::ThinVec; use rustc_errors::Diagnostic; use rustc_hir::def_id::{CrateNum, DefId, DefIndex, LocalDefId, LOCAL_CRATE}; use rustc_hir::definitions::DefPathHash; +use rustc_hir::definitions::Definitions; use rustc_index::vec::{Idx, IndexVec}; use rustc_serialize::{opaque, Decodable, Decoder, Encodable, Encoder}; use rustc_session::{CrateDisambiguator, Session}; @@ -21,6 +22,8 @@ use rustc_span::hygiene::{ use rustc_span::source_map::{SourceMap, StableSourceFileId}; use rustc_span::CachingSourceMapView; use rustc_span::{BytePos, ExpnData, SourceFile, Span, DUMMY_SP}; +use std::collections::hash_map::Entry; +use std::iter::FromIterator; use std::mem; const TAG_FILE_FOOTER: u128 = 0xC0FFEE_C0FFEE_C0FFEE_C0FFEE_C0FFEE; @@ -80,6 +83,31 @@ pub struct OnDiskCache<'sess> { expn_data: FxHashMap<u32, AbsoluteBytePos>, // Additional information used when decoding hygiene data. hygiene_context: HygieneDecodeContext, + // Maps `DefPathHash`es to their `RawDefId`s from the *previous* + // compilation session. This is used as an initial 'guess' when + // we try to map a `DefPathHash` to its `DefId` in the current compilation + // session. + foreign_def_path_hashes: FxHashMap<DefPathHash, RawDefId>, + + // The *next* compilation sessison's `foreign_def_path_hashes` - at + // the end of our current compilation session, this will get written + // out to the `foreign_def_path_hashes` field of the `Footer`, which + // will become `foreign_def_path_hashes` of the next compilation session. + // This stores any `DefPathHash` that we may need to map to a `DefId` + // during the next compilation session. + latest_foreign_def_path_hashes: Lock<FxHashMap<DefPathHash, RawDefId>>, + + // Maps `DefPathHashes` to their corresponding `LocalDefId`s for all + // local items in the current compilation session. This is only populated + // when we are in incremental mode and have loaded a pre-existing cache + // from disk, since this map is only used when deserializing a `DefPathHash` + // from the incremental cache. + local_def_path_hash_to_def_id: FxHashMap<DefPathHash, LocalDefId>, + // Caches all lookups of `DefPathHashes`, both for local and foreign + // definitions. A definition from the previous compilation session + // may no longer exist in the current compilation session, so + // we use `Option<DefId>` so that we can cache a lookup failure. + def_path_hash_to_def_id_cache: Lock<FxHashMap<DefPathHash, Option<DefId>>>, } // This type is used only for serialization and deserialization. @@ -95,6 +123,7 @@ struct Footer { syntax_contexts: FxHashMap<u32, AbsoluteBytePos>, // See `OnDiskCache.expn_data` expn_data: FxHashMap<u32, AbsoluteBytePos>, + foreign_def_path_hashes: FxHashMap<DefPathHash, RawDefId>, } type EncodedQueryResultIndex = Vec<(SerializedDepNodeIndex, AbsoluteBytePos)>; @@ -118,9 +147,36 @@ impl AbsoluteBytePos { } } +/// Represents a potentially invalid `DefId`. This is used during incremental +/// compilation to represent a `DefId` from the *previous* compilation session, +/// which may no longer be valid. This is used to help map a `DefPathHash` +/// to a `DefId` in the current compilation session. +#[derive(Encodable, Decodable, Copy, Clone, Debug)] +crate struct RawDefId { + // We deliberately do not use `CrateNum` and `DefIndex` + // here, since a crate/index from the previous compilation + // session may no longer exist. + pub krate: u32, + pub index: u32, +} + +fn make_local_def_path_hash_map(definitions: &Definitions) -> FxHashMap<DefPathHash, LocalDefId> { + FxHashMap::from_iter( + definitions + .def_path_table() + .all_def_path_hashes_and_def_ids(LOCAL_CRATE) + .map(|(hash, def_id)| (hash, def_id.as_local().unwrap())), + ) +} + impl<'sess> OnDiskCache<'sess> { /// Creates a new `OnDiskCache` instance from the serialized data in `data`. - pub fn new(sess: &'sess Session, data: Vec<u8>, start_pos: usize) -> Self { + pub fn new( + sess: &'sess Session, + data: Vec<u8>, + start_pos: usize, + definitions: &Definitions, + ) -> Self { debug_assert!(sess.opts.incremental.is_some()); // Wrap in a scope so we can borrow `data`. @@ -155,6 +211,10 @@ impl<'sess> OnDiskCache<'sess> { syntax_contexts: footer.syntax_contexts, expn_data: footer.expn_data, hygiene_context: Default::default(), + foreign_def_path_hashes: footer.foreign_def_path_hashes, + latest_foreign_def_path_hashes: Default::default(), + local_def_path_hash_to_def_id: make_local_def_path_hash_map(definitions), + def_path_hash_to_def_id_cache: Default::default(), } } @@ -173,6 +233,10 @@ impl<'sess> OnDiskCache<'sess> { syntax_contexts: FxHashMap::default(), expn_data: FxHashMap::default(), hygiene_context: Default::default(), + foreign_def_path_hashes: Default::default(), + latest_foreign_def_path_hashes: Default::default(), + local_def_path_hash_to_def_id: Default::default(), + def_path_hash_to_def_id_cache: Default::default(), } } @@ -200,6 +264,15 @@ impl<'sess> OnDiskCache<'sess> { (file_to_file_index, file_index_to_stable_id) }; + // Load everything into memory so we can write it out to the on-disk + // cache. The vast majority of cacheable query results should already + // be in memory, so this should be a cheap operation. + // Do this *before* we clone 'latest_foreign_def_path_hashes', since + // loading existing queries may cause us to create new DepNodes, which + // may in turn end up invoking `store_foreign_def_id_hash` + tcx.dep_graph.exec_cache_promotions(tcx); + + let latest_foreign_def_path_hashes = self.latest_foreign_def_path_hashes.lock().clone(); let hygiene_encode_context = HygieneEncodeContext::default(); let mut encoder = CacheEncoder { @@ -211,13 +284,9 @@ impl<'sess> OnDiskCache<'sess> { source_map: CachingSourceMapView::new(tcx.sess.source_map()), file_to_file_index, hygiene_context: &hygiene_encode_context, + latest_foreign_def_path_hashes, }; - // Load everything into memory so we can write it out to the on-disk - // cache. The vast majority of cacheable query results should already - // be in memory, so this should be a cheap operation. - tcx.dep_graph.exec_cache_promotions(tcx); - // Encode query results. let mut query_result_index = EncodedQueryResultIndex::new(); @@ -312,6 +381,9 @@ impl<'sess> OnDiskCache<'sess> { }, )?; + let foreign_def_path_hashes = + std::mem::take(&mut encoder.latest_foreign_def_path_hashes); + // `Encode the file footer. let footer_pos = encoder.position() as u64; encoder.encode_tagged( @@ -324,6 +396,7 @@ impl<'sess> OnDiskCache<'sess> { interpret_alloc_index, syntax_contexts, expn_data: expn_ids, + foreign_def_path_hashes, }, )?; @@ -374,6 +447,45 @@ impl<'sess> OnDiskCache<'sess> { debug_assert!(prev.is_none()); } + fn get_raw_def_id(&self, hash: &DefPathHash) -> Option<RawDefId> { + self.foreign_def_path_hashes.get(hash).copied() + } + + fn try_remap_cnum(&self, tcx: TyCtxt<'_>, cnum: u32) -> Option<CrateNum> { + let cnum_map = + self.cnum_map.get_or_init(|| Self::compute_cnum_map(tcx, &self.prev_cnums[..])); + debug!("try_remap_cnum({}): cnum_map={:?}", cnum, cnum_map); + + cnum_map[CrateNum::from_u32(cnum)] + } + + pub(crate) fn store_foreign_def_id_hash(&self, def_id: DefId, hash: DefPathHash) { + // We may overwrite an existing entry, but it will have the same value, + // so it's fine + self.latest_foreign_def_path_hashes + .lock() + .insert(hash, RawDefId { krate: def_id.krate.as_u32(), index: def_id.index.as_u32() }); + } + + /// If the given `hash` still exists in the current compilation, + /// calls `store_foreign_def_id` with its current `DefId`. + /// + /// Normally, `store_foreign_def_id_hash` can be called directly by + /// the dependency graph when we construct a `DepNode`. However, + /// when we re-use a deserialized `DepNode` from the previous compilation + /// session, we only have the `DefPathHash` available. This method is used + /// to that any `DepNode` that we re-use has a `DefPathHash` -> `RawId` written + /// out for usage in the next compilation session. + pub fn register_reused_dep_path_hash(&self, tcx: TyCtxt<'tcx>, hash: DefPathHash) { + // We can't simply copy the `RawDefId` from `foreign_def_path_hashes` to + // `latest_foreign_def_path_hashes`, since the `RawDefId` might have + // changed in the current compilation session (e.g. we've added/removed crates, + // or added/removed definitions before/after the target definition). + if let Some(def_id) = self.def_path_hash_to_def_id(tcx, hash) { + self.store_foreign_def_id_hash(def_id, hash); + } + } + /// Returns the cached query result if there is something in the cache for /// the given `SerializedDepNodeIndex`; otherwise returns `None`. crate fn try_load_query_result<'tcx, T>( @@ -481,6 +593,57 @@ impl<'sess> OnDiskCache<'sess> { map }) } + + /// Converts a `DefPathHash` to its corresponding `DefId` in the current compilation + /// session, if it still exists. This is used during incremental compilation to + /// turn a deserialized `DefPathHash` into its current `DefId`. + pub(crate) fn def_path_hash_to_def_id( + &self, + tcx: TyCtxt<'tcx>, + hash: DefPathHash, + ) -> Option<DefId> { + let mut cache = self.def_path_hash_to_def_id_cache.lock(); + match cache.entry(hash) { + Entry::Occupied(e) => *e.get(), + Entry::Vacant(e) => { + debug!("def_path_hash_to_def_id({:?})", hash); + // Check if the `DefPathHash` corresponds to a definition in the current + // crate + if let Some(def_id) = self.local_def_path_hash_to_def_id.get(&hash).cloned() { + let def_id = def_id.to_def_id(); + e.insert(Some(def_id)); + return Some(def_id); + } + // This `raw_def_id` represents the `DefId` of this `DefPathHash` in + // the *previous* compliation session. The `DefPathHash` includes the + // owning crate, so if the corresponding definition still exists in the + // current compilation session, the crate is guaranteed to be the same + // (otherwise, we would compute a different `DefPathHash`). + let raw_def_id = self.get_raw_def_id(&hash)?; + debug!("def_path_hash_to_def_id({:?}): raw_def_id = {:?}", hash, raw_def_id); + // If the owning crate no longer exists, the corresponding definition definitely + // no longer exists. + let krate = self.try_remap_cnum(tcx, raw_def_id.krate)?; + debug!("def_path_hash_to_def_id({:?}): krate = {:?}", hash, krate); + // If our `DefPathHash` corresponded to a definition in the local crate, + // we should have either found it in `local_def_path_hash_to_def_id`, or + // never attempted to load it in the first place. Any query result or `DepNode` + // that references a local `DefId` should depend on some HIR-related `DepNode`. + // If a local definition is removed/modified such that its old `DefPathHash` + // no longer has a corresponding definition, that HIR-related `DepNode` should + // end up red. This should prevent us from ever calling + // `tcx.def_path_hash_to_def_id`, since we'll end up recomputing any + // queries involved. + debug_assert_ne!(krate, LOCAL_CRATE); + // Try to find a definition in the current session, using the previous `DefIndex` + // as an initial guess. + let opt_def_id = tcx.cstore.def_path_hash_to_def_id(krate, raw_def_id.index, hash); + debug!("def_path_to_def_id({:?}): opt_def_id = {:?}", hash, opt_def_id); + e.insert(opt_def_id); + opt_def_id + } + } + } } //- DECODING ------------------------------------------------------------------- @@ -713,7 +876,17 @@ impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for DefId { let def_path_hash = DefPathHash::decode(d)?; // Using the `DefPathHash`, we can lookup the new `DefId`. - Ok(d.tcx().def_path_hash_to_def_id.as_ref().unwrap()[&def_path_hash]) + // Subtle: We only encode a `DefId` as part of a query result. + // If we get to this point, then all of the query inputs were green, + // which means that the definition with this hash is guaranteed to + // still exist in the current compilation session. + Ok(d.tcx() + .queries + .on_disk_cache + .as_ref() + .unwrap() + .def_path_hash_to_def_id(d.tcx(), def_path_hash) + .unwrap()) } } @@ -773,6 +946,7 @@ struct CacheEncoder<'a, 'tcx, E: OpaqueEncoder> { source_map: CachingSourceMapView<'tcx>, file_to_file_index: FxHashMap<*const SourceFile, SourceFileIndex>, hygiene_context: &'a HygieneEncodeContext, + latest_foreign_def_path_hashes: FxHashMap<DefPathHash, RawDefId>, } impl<'a, 'tcx, E> CacheEncoder<'a, 'tcx, E> @@ -895,6 +1069,15 @@ where { fn encode(&self, s: &mut CacheEncoder<'a, 'tcx, E>) -> Result<(), E::Error> { let def_path_hash = s.tcx.def_path_hash(*self); + // Store additional information when we encode a foreign `DefId`, + // so that we can map its `DefPathHash` back to a `DefId` in the next + // compilation session. + if !self.is_local() { + s.latest_foreign_def_path_hashes.insert( + def_path_hash, + RawDefId { krate: self.krate.as_u32(), index: self.index.as_u32() }, + ); + } def_path_hash.encode(s) } } diff --git a/compiler/rustc_middle/src/ty/query/plumbing.rs b/compiler/rustc_middle/src/ty/query/plumbing.rs index d038695283c..d0730bd121c 100644 --- a/compiler/rustc_middle/src/ty/query/plumbing.rs +++ b/compiler/rustc_middle/src/ty/query/plumbing.rs @@ -128,7 +128,7 @@ impl<'tcx> TyCtxt<'tcx> { pub fn try_print_query_stack(handler: &Handler, num_frames: Option<usize>) { eprintln!("query stack during panic:"); - // Be careful reyling on global state here: this code is called from + // Be careful relying on global state here: this code is called from // a panic hook, which means that the global `Handler` may be in a weird // state if it was responsible for triggering the panic. let mut i = 0; @@ -507,10 +507,11 @@ macro_rules! define_queries_struct { (tcx: $tcx:tt, input: ($(([$($modifiers:tt)*] [$($attr:tt)*] [$name:ident]))*)) => { pub struct Queries<$tcx> { - /// This provides access to the incrimental comilation on-disk cache for query results. + /// This provides access to the incremental compilation on-disk cache for query results. /// Do not access this directly. It is only meant to be used by /// `DepGraph::try_mark_green()` and the query infrastructure. - pub(crate) on_disk_cache: OnDiskCache<'tcx>, + /// This is `None` if we are not incremental compilation mode + pub(crate) on_disk_cache: Option<OnDiskCache<'tcx>>, providers: IndexVec<CrateNum, Providers>, fallback_extern_providers: Box<Providers>, @@ -526,7 +527,7 @@ macro_rules! define_queries_struct { pub(crate) fn new( providers: IndexVec<CrateNum, Providers>, fallback_extern_providers: Providers, - on_disk_cache: OnDiskCache<'tcx>, + on_disk_cache: Option<OnDiskCache<'tcx>>, ) -> Self { Queries { providers, diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 4bf16436855..fc9dcdf1775 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -1106,10 +1106,7 @@ impl<T> Binder<T> { impl<T> Binder<Option<T>> { pub fn transpose(self) -> Option<Binder<T>> { - match self.0 { - Some(v) => Some(Binder(v)), - None => None, - } + self.0.map(Binder) } } @@ -1292,53 +1289,6 @@ impl<'tcx> ParamConst { } } -rustc_index::newtype_index! { - /// A [De Bruijn index][dbi] is a standard means of representing - /// regions (and perhaps later types) in a higher-ranked setting. In - /// particular, imagine a type like this: - /// - /// for<'a> fn(for<'b> fn(&'b isize, &'a isize), &'a char) - /// ^ ^ | | | - /// | | | | | - /// | +------------+ 0 | | - /// | | | - /// +----------------------------------+ 1 | - /// | | - /// +----------------------------------------------+ 0 - /// - /// In this type, there are two binders (the outer fn and the inner - /// fn). We need to be able to determine, for any given region, which - /// fn type it is bound by, the inner or the outer one. There are - /// various ways you can do this, but a De Bruijn index is one of the - /// more convenient and has some nice properties. The basic idea is to - /// count the number of binders, inside out. Some examples should help - /// clarify what I mean. - /// - /// Let's start with the reference type `&'b isize` that is the first - /// argument to the inner function. This region `'b` is assigned a De - /// Bruijn index of 0, meaning "the innermost binder" (in this case, a - /// fn). The region `'a` that appears in the second argument type (`&'a - /// isize`) would then be assigned a De Bruijn index of 1, meaning "the - /// second-innermost binder". (These indices are written on the arrays - /// in the diagram). - /// - /// What is interesting is that De Bruijn index attached to a particular - /// variable will vary depending on where it appears. For example, - /// the final type `&'a char` also refers to the region `'a` declared on - /// the outermost fn. But this time, this reference is not nested within - /// any other binders (i.e., it is not an argument to the inner fn, but - /// rather the outer one). Therefore, in this case, it is assigned a - /// De Bruijn index of 0, because the innermost binder in that location - /// is the outer fn. - /// - /// [dbi]: https://en.wikipedia.org/wiki/De_Bruijn_index - #[derive(HashStable)] - pub struct DebruijnIndex { - DEBUG_FORMAT = "DebruijnIndex({})", - const INNERMOST = 0, - } -} - pub type Region<'tcx> = &'tcx RegionKind; /// Representation of regions. Note that the NLL checker uses a distinct @@ -1453,7 +1403,7 @@ pub enum RegionKind { /// Region bound in a function scope, which will be substituted when the /// function is called. - ReLateBound(DebruijnIndex, BoundRegion), + ReLateBound(ty::DebruijnIndex, BoundRegion), /// When checking a function body, the types of all arguments and so forth /// that refer to bound region parameters are modified to refer to free @@ -1617,65 +1567,6 @@ impl<'tcx> PolyExistentialProjection<'tcx> { } } -impl DebruijnIndex { - /// Returns the resulting index when this value is moved into - /// `amount` number of new binders. So, e.g., if you had - /// - /// for<'a> fn(&'a x) - /// - /// and you wanted to change it to - /// - /// for<'a> fn(for<'b> fn(&'a x)) - /// - /// you would need to shift the index for `'a` into a new binder. - #[must_use] - pub fn shifted_in(self, amount: u32) -> DebruijnIndex { - DebruijnIndex::from_u32(self.as_u32() + amount) - } - - /// Update this index in place by shifting it "in" through - /// `amount` number of binders. - pub fn shift_in(&mut self, amount: u32) { - *self = self.shifted_in(amount); - } - - /// Returns the resulting index when this value is moved out from - /// `amount` number of new binders. - #[must_use] - pub fn shifted_out(self, amount: u32) -> DebruijnIndex { - DebruijnIndex::from_u32(self.as_u32() - amount) - } - - /// Update in place by shifting out from `amount` binders. - pub fn shift_out(&mut self, amount: u32) { - *self = self.shifted_out(amount); - } - - /// Adjusts any De Bruijn indices so as to make `to_binder` the - /// innermost binder. That is, if we have something bound at `to_binder`, - /// it will now be bound at INNERMOST. This is an appropriate thing to do - /// when moving a region out from inside binders: - /// - /// ``` - /// for<'a> fn(for<'b> for<'c> fn(&'a u32), _) - /// // Binder: D3 D2 D1 ^^ - /// ``` - /// - /// Here, the region `'a` would have the De Bruijn index D3, - /// because it is the bound 3 binders out. However, if we wanted - /// to refer to that region `'a` in the second argument (the `_`), - /// those two binders would not be in scope. In that case, we - /// might invoke `shift_out_to_binder(D3)`. This would adjust the - /// De Bruijn index of `'a` to D1 (the innermost binder). - /// - /// If we invoke `shift_out_to_binder` and the region is in fact - /// bound by one of the binders we are shifting out of, that is an - /// error (and should fail an assertion failure). - pub fn shifted_out_to_binder(self, to_binder: DebruijnIndex) -> Self { - self.shifted_out(to_binder.as_u32() - INNERMOST.as_u32()) - } -} - /// Region utilities impl RegionKind { /// Is this region named by the user? @@ -1706,7 +1597,7 @@ impl RegionKind { } } - pub fn bound_at_or_above_binder(&self, index: DebruijnIndex) -> bool { + pub fn bound_at_or_above_binder(&self, index: ty::DebruijnIndex) -> bool { match *self { ty::ReLateBound(debruijn, _) => debruijn >= index, _ => false, @@ -1959,27 +1850,22 @@ impl<'tcx> TyS<'tcx> { } } - pub fn simd_type(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { - match self.kind() { - Adt(def, substs) => def.non_enum_variant().fields[0].ty(tcx, substs), - _ => bug!("`simd_type` called on invalid type"), - } - } - - pub fn simd_size(&self, _tcx: TyCtxt<'tcx>) -> u64 { - // Parameter currently unused, but probably needed in the future to - // allow `#[repr(simd)] struct Simd<T, const N: usize>([T; N]);`. - match self.kind() { - Adt(def, _) => def.non_enum_variant().fields.len() as u64, - _ => bug!("`simd_size` called on invalid type"), - } - } - pub fn simd_size_and_type(&self, tcx: TyCtxt<'tcx>) -> (u64, Ty<'tcx>) { match self.kind() { Adt(def, substs) => { let variant = def.non_enum_variant(); - (variant.fields.len() as u64, variant.fields[0].ty(tcx, substs)) + let f0_ty = variant.fields[0].ty(tcx, substs); + + match f0_ty.kind() { + Array(f0_elem_ty, f0_len) => { + // FIXME(repr_simd): https://github.com/rust-lang/rust/pull/78863#discussion_r522784112 + // The way we evaluate the `N` in `[T; N]` here only works since we use + // `simd_size_and_type` post-monomorphization. It will probably start to ICE + // if we use it in generic code. See the `simd-array-trait` ui test. + (f0_len.eval_usize(tcx, ParamEnv::empty()) as u64, f0_elem_ty) + } + _ => (variant.fields.len() as u64, f0_ty), + } } _ => bug!("`simd_size_and_type` called on invalid type"), } @@ -2182,6 +2068,15 @@ impl<'tcx> TyS<'tcx> { } } + /// Get the `i`-th element of a tuple. + /// Panics when called on anything but a tuple. + pub fn tuple_element_ty(&self, i: usize) -> Option<Ty<'tcx>> { + match self.kind() { + Tuple(substs) => substs.iter().nth(i).map(|field| field.expect_ty()), + _ => bug!("tuple_fields called on non-tuple"), + } + } + /// If the type contains variants, returns the valid range of variant indices. // // FIXME: This requires the optimized MIR in the case of generators. @@ -2221,13 +2116,44 @@ impl<'tcx> TyS<'tcx> { } /// Returns the type of the discriminant of this type. - pub fn discriminant_ty(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { + pub fn discriminant_ty(&'tcx self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { match self.kind() { ty::Adt(adt, _) if adt.is_enum() => adt.repr.discr_type().to_ty(tcx), ty::Generator(_, substs, _) => substs.as_generator().discr_ty(tcx), - _ => { - // This can only be `0`, for now, so `u8` will suffice. - tcx.types.u8 + + ty::Param(_) | ty::Projection(_) | ty::Opaque(..) | ty::Infer(ty::TyVar(_)) => { + let assoc_items = + tcx.associated_items(tcx.lang_items().discriminant_kind_trait().unwrap()); + let discriminant_def_id = assoc_items.in_definition_order().next().unwrap().def_id; + tcx.mk_projection(discriminant_def_id, tcx.mk_substs([self.into()].iter())) + } + + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Adt(..) + | ty::Foreign(_) + | ty::Str + | ty::Array(..) + | ty::Slice(_) + | ty::RawPtr(_) + | ty::Ref(..) + | ty::FnDef(..) + | ty::FnPtr(..) + | ty::Dynamic(..) + | ty::Closure(..) + | ty::GeneratorWitness(..) + | ty::Never + | ty::Tuple(_) + | ty::Error(_) + | ty::Infer(IntVar(_) | FloatVar(_)) => tcx.types.u8, + + ty::Bound(..) + | ty::Placeholder(_) + | ty::Infer(FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { + bug!("`discriminant_ty` applied to unexpected type: {:?}", self) } } } diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index e23c3f51967..25787f005aa 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -18,7 +18,7 @@ use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_macros::HashStable; -use rustc_span::Span; +use rustc_span::{Span, DUMMY_SP}; use rustc_target::abi::{Integer, Size, TargetDataLayout}; use smallvec::SmallVec; use std::{cmp, fmt}; @@ -221,7 +221,13 @@ impl<'tcx> TyCtxt<'tcx> { mut ty: Ty<'tcx>, normalize: impl Fn(Ty<'tcx>) -> Ty<'tcx>, ) -> Ty<'tcx> { - loop { + for iteration in 0.. { + if !self.sess.recursion_limit().value_within_limit(iteration) { + return self.ty_error_with_message( + DUMMY_SP, + &format!("reached the recursion limit finding the struct tail for {}", ty), + ); + } match *ty.kind() { ty::Adt(def, substs) => { if !def.is_struct() { |
