Skip to content

Commit

Permalink
Add query to avoid name comparison in leaf_def
Browse files Browse the repository at this point in the history
  • Loading branch information
matthewjasper authored and camelid committed Jan 7, 2022
1 parent 1b057a3 commit 3b7d496
Show file tree
Hide file tree
Showing 8 changed files with 114 additions and 89 deletions.
26 changes: 26 additions & 0 deletions compiler/rustc_middle/src/query/mod.rs
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
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: 2 additions & 3 deletions compiler/rustc_monomorphize/src/collector.rs
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
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
28 changes: 14 additions & 14 deletions compiler/rustc_trait_selection/src/traits/project.rs
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
34 changes: 20 additions & 14 deletions compiler/rustc_trait_selection/src/traits/wf.rs
Expand Up @@ -197,14 +197,13 @@ fn extend_cause_with_original_assoc_item_obligation<'tcx>(
item: Option<&hir::Item<'tcx>>,
cause: &mut traits::ObligationCause<'tcx>,
pred: &ty::Predicate<'tcx>,
mut trait_assoc_items: impl Iterator<Item = &'tcx ty::AssocItem>,
) {
debug!(
"extended_cause_with_original_assoc_item_obligation {:?} {:?} {:?} {:?}",
trait_ref, item, cause, pred
);
let items = match item {
Some(hir::Item { kind: hir::ItemKind::Impl(impl_), .. }) => impl_.items,
let (items, impl_def_id) = match item {
Some(hir::Item { kind: hir::ItemKind::Impl(impl_), def_id, .. }) => (impl_.items, *def_id),
_ => return,
};
let fix_span =
Expand All @@ -222,11 +221,16 @@ fn extend_cause_with_original_assoc_item_obligation<'tcx>(
// `src/test/ui/associated-types/point-at-type-on-obligation-failure.rs` and
// `traits-assoc-type-in-supertrait-bad.rs`.
if let ty::Projection(projection_ty) = proj.ty.kind() {
let trait_assoc_item = tcx.associated_item(projection_ty.item_def_id);
if let Some(impl_item_span) =
items.iter().find(|item| item.ident == trait_assoc_item.ident).map(fix_span)
if let Some(&impl_item_id) =
tcx.impl_item_implementor_ids(impl_def_id).get(&projection_ty.item_def_id)
{
cause.span = impl_item_span;
if let Some(impl_item_span) = items
.iter()
.find(|item| item.id.def_id.to_def_id() == impl_item_id)
.map(fix_span)
{
cause.span = impl_item_span;
}
}
}
}
Expand All @@ -235,13 +239,16 @@ fn extend_cause_with_original_assoc_item_obligation<'tcx>(
// can be seen in `ui/associated-types/point-at-type-on-obligation-failure-2.rs`.
debug!("extended_cause_with_original_assoc_item_obligation trait proj {:?}", pred);
if let ty::Projection(ty::ProjectionTy { item_def_id, .. }) = *pred.self_ty().kind() {
if let Some(impl_item_span) = trait_assoc_items
.find(|i| i.def_id == item_def_id)
.and_then(|trait_assoc_item| {
items.iter().find(|i| i.ident == trait_assoc_item.ident).map(fix_span)
})
if let Some(&impl_item_id) =
tcx.impl_item_implementor_ids(impl_def_id).get(&item_def_id)
{
cause.span = impl_item_span;
if let Some(impl_item_span) = items
.iter()
.find(|item| item.id.def_id.to_def_id() == impl_item_id)
.map(fix_span)
{
cause.span = impl_item_span;
}
}
}
}
Expand Down Expand Up @@ -312,7 +319,6 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
item,
&mut cause,
&obligation.predicate,
tcx.associated_items(trait_ref.def_id).in_definition_order(),
);
traits::Obligation::with_depth(cause, depth, param_env, obligation.predicate)
};
Expand Down
9 changes: 9 additions & 0 deletions compiler/rustc_ty_utils/src/assoc.rs
@@ -1,3 +1,4 @@
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_hir::def_id::{DefId, LocalDefId};
Expand All @@ -8,6 +9,7 @@ pub fn provide(providers: &mut ty::query::Providers) {
associated_item,
associated_item_def_ids,
associated_items,
impl_item_implementor_ids,
trait_of_item,
..*providers
};
Expand All @@ -32,6 +34,13 @@ fn associated_items(tcx: TyCtxt<'_>, def_id: DefId) -> ty::AssocItems<'_> {
ty::AssocItems::new(items)
}

fn impl_item_implementor_ids(tcx: TyCtxt<'_>, impl_id: DefId) -> FxHashMap<DefId, DefId> {
tcx.associated_items(impl_id)
.in_definition_order()
.filter_map(|item| item.trait_item_def_id.map(|trait_item| (trait_item, item.def_id)))
.collect()
}

/// If the given `DefId` describes an item belonging to a trait,
/// returns the `DefId` of the trait that the trait item belongs to;
/// otherwise, returns `None`.
Expand Down

0 comments on commit 3b7d496

Please sign in to comment.