Skip to content

Commit

Permalink
Auto merge of #90639 - matthewjasper:leaf-def-cache, r=cjgillot
Browse files Browse the repository at this point in the history
Add a query for resolving an impl item from the trait item

This makes finding the item in an impl that implements a given trait item a query. This is for a few reasons:

- To slightly improve performance
- To avoid having to do name resolution during monomorphisation
- To make it easier to implement potential future features that create anonymous associated items
  • Loading branch information
bors committed Jan 8, 2022
2 parents 66f64a4 + 9d625bc commit 488acf8
Show file tree
Hide file tree
Showing 25 changed files with 483 additions and 448 deletions.
15 changes: 15 additions & 0 deletions compiler/rustc_metadata/src/rmeta/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1008,6 +1008,10 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
self.get_impl_data(id).constness
}

fn get_trait_item_def_id(&self, id: DefIndex) -> Option<DefId> {
self.root.tables.trait_item_def_id.get(self, id).map(|d| d.decode(self))
}

fn get_coerce_unsized_info(&self, id: DefIndex) -> Option<ty::adjustment::CoerceUnsizedInfo> {
self.get_impl_data(id).coerce_unsized_info
}
Expand Down Expand Up @@ -1258,6 +1262,16 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
}
}

fn get_associated_item_def_ids(&self, tcx: TyCtxt<'tcx>, id: DefIndex) -> &'tcx [DefId] {
if let Some(children) = self.root.tables.children.get(self, id) {
tcx.arena.alloc_from_iter(
children.decode((self, tcx.sess)).map(|child_index| self.local_def_id(child_index)),
)
} else {
&[]
}
}

fn get_associated_item(&self, id: DefIndex, sess: &Session) -> ty::AssocItem {
let def_key = self.def_key(id);
let parent = self.local_def_id(def_key.parent.unwrap());
Expand All @@ -1279,6 +1293,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
vis: self.get_visibility(id),
defaultness: container.defaultness(),
def_id: self.local_def_id(id),
trait_item_def_id: self.get_trait_item_def_id(id),
container: container.with_def_id(parent),
fn_has_self_parameter: has_self,
}
Expand Down
7 changes: 1 addition & 6 deletions compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,12 +103,7 @@ provide! { <'tcx> tcx, def_id, other, cdata,
tcx.calculate_dtor(def_id, |_,_| Ok(()))
}
variances_of => { tcx.arena.alloc_from_iter(cdata.get_item_variances(def_id.index)) }
associated_item_def_ids => {
let mut result = SmallVec::<[_; 8]>::new();
cdata.each_child_of_item(def_id.index,
|child| result.push(child.res.def_id()), tcx.sess);
tcx.arena.alloc_slice(&result)
}
associated_item_def_ids => { cdata.get_associated_item_def_ids(tcx, def_id.index) }
associated_item => { cdata.get_associated_item(def_id.index, tcx.sess) }
impl_trait_ref => { cdata.get_impl_trait(def_id.index, tcx) }
impl_polarity => { cdata.get_impl_polarity(def_id.index) }
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_metadata/src/rmeta/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1294,6 +1294,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
}
self.encode_ident_span(def_id, impl_item.ident);
self.encode_item_type(def_id);
if let Some(trait_item_def_id) = impl_item.trait_item_def_id {
record!(self.tables.trait_item_def_id[def_id] <- trait_item_def_id);
}
if impl_item.kind == ty::AssocKind::Fn {
record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id));
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_metadata/src/rmeta/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,7 @@ define_tables! {
ty: Table<DefIndex, Lazy!(Ty<'tcx>)>,
fn_sig: Table<DefIndex, Lazy!(ty::PolyFnSig<'tcx>)>,
impl_trait_ref: Table<DefIndex, Lazy!(ty::TraitRef<'tcx>)>,
trait_item_def_id: Table<DefIndex, Lazy<DefId>>,
inherent_impls: Table<DefIndex, Lazy<[DefIndex]>>,
variances: Table<DefIndex, Lazy<[ty::Variance]>>,
generics: Table<DefIndex, Lazy<ty::Generics>>,
Expand Down
26 changes: 26 additions & 0 deletions compiler/rustc_middle/src/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -630,6 +630,32 @@ rustc_queries! {
desc { |tcx| "collecting associated items of {}", tcx.def_path_str(key) }
}

/// Maps from associated items on a trait to the corresponding associated
/// item on the impl specified by `impl_id`.
///
/// For example, with the following code
///
/// ```
/// struct Type {}
/// // DefId
/// trait Trait { // trait_id
/// fn f(); // trait_f
/// fn g() {} // trait_g
/// }
///
/// impl Trait for Type { // impl_id
/// fn f() {} // impl_f
/// fn g() {} // impl_g
/// }
/// ```
///
/// The map returned for `tcx.impl_item_implementor_ids(impl_id)` would be
///`{ trait_f: impl_f, trait_g: impl_g }`
query impl_item_implementor_ids(impl_id: DefId) -> FxHashMap<DefId, DefId> {
desc { |tcx| "comparing impl items against trait for {}", tcx.def_path_str(impl_id) }
storage(ArenaCacheSelector<'tcx>)
}

/// Given an `impl_id`, return the trait it implements.
/// Return `None` if this is an inherent impl.
query impl_trait_ref(impl_id: DefId) -> Option<ty::TraitRef<'tcx>> {
Expand Down
45 changes: 16 additions & 29 deletions compiler/rustc_middle/src/traits/specialization_graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ use crate::ty::{self, TyCtxt};
use rustc_data_structures::fx::FxIndexMap;
use rustc_errors::ErrorReported;
use rustc_hir::def_id::{DefId, DefIdMap};
use rustc_span::symbol::Ident;

/// A per-trait graph of impls in specialization order. At the moment, this
/// graph forms a tree rooted with the trait itself, with all other nodes
Expand Down Expand Up @@ -75,34 +74,28 @@ pub enum Node {
Trait(DefId),
}

impl<'tcx> Node {
impl Node {
pub fn is_from_trait(&self) -> bool {
matches!(self, Node::Trait(..))
}

/// Iterate over the items defined directly by the given (impl or trait) node.
pub fn items(&self, tcx: TyCtxt<'tcx>) -> impl 'tcx + Iterator<Item = &'tcx ty::AssocItem> {
tcx.associated_items(self.def_id()).in_definition_order()
}

/// Finds an associated item defined in this node.
/// Trys to find the associated item that implements `trait_item_def_id`
/// defined in this node.
///
/// If this returns `None`, the item can potentially still be found in
/// parents of this node.
pub fn item(
pub fn item<'tcx>(
&self,
tcx: TyCtxt<'tcx>,
trait_item_name: Ident,
trait_item_kind: ty::AssocKind,
trait_def_id: DefId,
) -> Option<ty::AssocItem> {
tcx.associated_items(self.def_id())
.filter_by_name_unhygienic(trait_item_name.name)
.find(move |impl_item| {
trait_item_kind == impl_item.kind
&& tcx.hygienic_eq(impl_item.ident, trait_item_name, trait_def_id)
})
.copied()
trait_item_def_id: DefId,
) -> Option<&'tcx ty::AssocItem> {
match *self {
Node::Trait(_) => Some(tcx.associated_item(trait_item_def_id)),
Node::Impl(impl_def_id) => {
let id = tcx.impl_item_implementor_ids(impl_def_id).get(&trait_item_def_id)?;
Some(tcx.associated_item(*id))
}
}
}

pub fn def_id(&self) -> DefId {
Expand Down Expand Up @@ -181,17 +174,11 @@ impl LeafDef {
impl<'tcx> Ancestors<'tcx> {
/// Finds the bottom-most (ie. most specialized) definition of an associated
/// item.
pub fn leaf_def(
mut self,
tcx: TyCtxt<'tcx>,
trait_item_name: Ident,
trait_item_kind: ty::AssocKind,
) -> Option<LeafDef> {
let trait_def_id = self.trait_def_id;
pub fn leaf_def(mut self, tcx: TyCtxt<'tcx>, trait_item_def_id: DefId) -> Option<LeafDef> {
let mut finalizing_node = None;

self.find_map(|node| {
if let Some(item) = node.item(tcx, trait_item_name, trait_item_kind, trait_def_id) {
if let Some(item) = node.item(tcx, trait_item_def_id) {
if finalizing_node.is_none() {
let is_specializable = item.defaultness.is_default()
|| tcx.impl_defaultness(node.def_id()).is_default();
Expand All @@ -201,7 +188,7 @@ impl<'tcx> Ancestors<'tcx> {
}
}

Some(LeafDef { item, defining_node: node, finalizing_node })
Some(LeafDef { item: *item, defining_node: node, finalizing_node })
} else {
// Item not mentioned. This "finalizes" any defaulted item provided by an ancestor.
finalizing_node = Some(node);
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_middle/src/ty/assoc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ impl AssocItemContainer {
}
}

/// Information about an associated item
#[derive(Copy, Clone, Debug, PartialEq, HashStable, Eq, Hash)]
pub struct AssocItem {
pub def_id: DefId,
Expand All @@ -50,6 +51,10 @@ pub struct AssocItem {
pub defaultness: hir::Defaultness,
pub container: AssocItemContainer,

/// If this is an item in an impl of a trait then this is the `DefId` of
/// the associated item on the trait that this implements.
pub trait_item_def_id: Option<DefId>,

/// Whether this is a method with an explicit self
/// as its first parameter, allowing method calls.
pub fn_has_self_parameter: bool,
Expand Down
5 changes: 2 additions & 3 deletions compiler/rustc_monomorphize/src/collector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1310,10 +1310,9 @@ fn create_mono_items_for_default_impls<'tcx>(
if let Some(trait_ref) = tcx.impl_trait_ref(item.def_id) {
let param_env = ty::ParamEnv::reveal_all();
let trait_ref = tcx.normalize_erasing_regions(param_env, trait_ref);
let overridden_methods: FxHashSet<_> =
impl_.items.iter().map(|iiref| iiref.ident.normalize_to_macros_2_0()).collect();
let overridden_methods = tcx.impl_item_implementor_ids(item.def_id);
for method in tcx.provided_trait_methods(trait_ref.def_id) {
if overridden_methods.contains(&method.ident.normalize_to_macros_2_0()) {
if overridden_methods.contains_key(&method.def_id) {
continue;
}

Expand Down
13 changes: 8 additions & 5 deletions compiler/rustc_passes/src/check_const.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,26 +93,29 @@ impl<'tcx> hir::itemlikevisit::ItemLikeVisitor<'tcx> for CheckConstTraitVisitor<
for trait_item in self.tcx.associated_items(trait_def_id).in_definition_order()
{
if let ty::AssocItem {
kind: ty::AssocKind::Fn, ident, defaultness, ..
} = trait_item
kind: ty::AssocKind::Fn,
defaultness,
def_id: trait_item_id,
..
} = *trait_item
{
// we can ignore functions that do not have default bodies:
// if those are unimplemented it will be catched by typeck.
if !defaultness.has_value()
|| self
.tcx
.has_attr(trait_item.def_id, sym::default_method_body_is_const)
.has_attr(trait_item_id, sym::default_method_body_is_const)
{
continue;
}

let is_implemented = ancestors
.leaf_def(self.tcx, trait_item.ident, trait_item.kind)
.leaf_def(self.tcx, trait_item_id)
.map(|node_item| !node_item.defining_node.is_from_trait())
.unwrap_or(false);

if !is_implemented {
to_implement.push(ident.to_string());
to_implement.push(self.tcx.item_name(trait_item_id).to_string());
}
}
}
Expand Down
19 changes: 6 additions & 13 deletions compiler/rustc_passes/src/stability.rs
Original file line number Diff line number Diff line change
Expand Up @@ -794,19 +794,12 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
}
}

if let Res::Def(DefKind::Trait, trait_did) = t.path.res {
for impl_item_ref in items {
let impl_item = self.tcx.hir().impl_item(impl_item_ref.id);
let trait_item_def_id = self
.tcx
.associated_items(trait_did)
.filter_by_name_unhygienic(impl_item.ident.name)
.next()
.map(|item| item.def_id);
if let Some(def_id) = trait_item_def_id {
// Pass `None` to skip deprecation warnings.
self.tcx.check_stability(def_id, None, impl_item.span, None);
}
for impl_item_ref in items {
let impl_item = self.tcx.associated_item(impl_item_ref.id.def_id);

if let Some(def_id) = impl_item.trait_item_def_id {
// Pass `None` to skip deprecation warnings.
self.tcx.check_stability(def_id, None, impl_item_ref.span, None);
}
}
}
Expand Down
12 changes: 5 additions & 7 deletions compiler/rustc_save_analysis/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -710,13 +710,11 @@ impl<'tcx> SaveContext<'tcx> {
}
Res::Def(HirDefKind::AssocFn, decl_id) => {
let def_id = if decl_id.is_local() {
let ti = self.tcx.associated_item(decl_id);

self.tcx
.associated_items(ti.container.id())
.filter_by_name_unhygienic(ti.ident.name)
.find(|item| item.defaultness.has_value())
.map(|item| item.def_id)
if self.tcx.associated_item(decl_id).defaultness.has_value() {
Some(decl_id)
} else {
None
}
} else {
None
};
Expand Down
28 changes: 14 additions & 14 deletions compiler/rustc_trait_selection/src/traits/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1883,7 +1883,6 @@ fn assoc_ty_def(
assoc_ty_def_id: DefId,
) -> Result<specialization_graph::LeafDef, ErrorReported> {
let tcx = selcx.tcx();
let assoc_ty_name = tcx.associated_item(assoc_ty_def_id).ident;
let trait_def_id = tcx.impl_trait_ref(impl_def_id).unwrap().def_id;
let trait_def = tcx.trait_def(trait_def_id);

Expand All @@ -1893,21 +1892,18 @@ fn assoc_ty_def(
// for the associated item at the given impl.
// If there is no such item in that impl, this function will fail with a
// cycle error if the specialization graph is currently being built.
let impl_node = specialization_graph::Node::Impl(impl_def_id);
for item in impl_node.items(tcx) {
if matches!(item.kind, ty::AssocKind::Type)
&& tcx.hygienic_eq(item.ident, assoc_ty_name, trait_def_id)
{
return Ok(specialization_graph::LeafDef {
item: *item,
defining_node: impl_node,
finalizing_node: if item.defaultness.is_default() { None } else { Some(impl_node) },
});
}
if let Some(&impl_item_id) = tcx.impl_item_implementor_ids(impl_def_id).get(&assoc_ty_def_id) {
let item = tcx.associated_item(impl_item_id);
let impl_node = specialization_graph::Node::Impl(impl_def_id);
return Ok(specialization_graph::LeafDef {
item: *item,
defining_node: impl_node,
finalizing_node: if item.defaultness.is_default() { None } else { Some(impl_node) },
});
}

let ancestors = trait_def.ancestors(tcx, impl_def_id)?;
if let Some(assoc_item) = ancestors.leaf_def(tcx, assoc_ty_name, ty::AssocKind::Type) {
if let Some(assoc_item) = ancestors.leaf_def(tcx, assoc_ty_def_id) {
Ok(assoc_item)
} else {
// This is saying that neither the trait nor
Expand All @@ -1916,7 +1912,11 @@ fn assoc_ty_def(
// could only arise through a compiler bug --
// if the user wrote a bad item name, it
// should have failed in astconv.
bug!("No associated type `{}` for {}", assoc_ty_name, tcx.def_path_str(impl_def_id))
bug!(
"No associated type `{}` for {}",
tcx.item_name(assoc_ty_def_id),
tcx.def_path_str(impl_def_id)
)
}
}

Expand Down
Loading

0 comments on commit 488acf8

Please sign in to comment.