use rustc_data_structures::fx::FxIndexSet;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId};
use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::{self as hir, HirId};
use rustc_index::IndexVec;
use rustc_middle::query::Providers;
use rustc_middle::ty::{self, ImplTraitInTraitData, TyCtxt, TyCtxtFeed};
use rustc_span::symbol::kw;
pub(crate) fn provide(providers: &mut Providers) {
*providers = Providers {
associated_item,
associated_item_def_ids,
associated_items,
associated_types_for_impl_traits_in_associated_fn,
associated_type_for_impl_trait_in_trait,
impl_item_implementor_ids,
..*providers
};
}
fn associated_item_def_ids(tcx: TyCtxt<'_>, def_id: LocalDefId) -> &[DefId] {
let item = tcx.hir().expect_item(def_id);
match item.kind {
hir::ItemKind::Trait(.., trait_item_refs) => {
tcx.arena.alloc_from_iter(
trait_item_refs
.iter()
.map(|trait_item_ref| trait_item_ref.id.owner_id.to_def_id())
.chain(
trait_item_refs
.iter()
.filter(|trait_item_ref| {
matches!(trait_item_ref.kind, hir::AssocItemKind::Fn { .. })
})
.flat_map(|trait_item_ref| {
let trait_fn_def_id = trait_item_ref.id.owner_id.def_id.to_def_id();
tcx.associated_types_for_impl_traits_in_associated_fn(
trait_fn_def_id,
)
})
.copied(),
),
)
}
hir::ItemKind::Impl(impl_) => {
tcx.arena.alloc_from_iter(
impl_
.items
.iter()
.map(|impl_item_ref| impl_item_ref.id.owner_id.to_def_id())
.chain(impl_.of_trait.iter().flat_map(|_| {
impl_
.items
.iter()
.filter(|impl_item_ref| {
matches!(impl_item_ref.kind, hir::AssocItemKind::Fn { .. })
})
.flat_map(|impl_item_ref| {
let impl_fn_def_id = impl_item_ref.id.owner_id.def_id.to_def_id();
tcx.associated_types_for_impl_traits_in_associated_fn(
impl_fn_def_id,
)
})
.copied()
})),
)
}
_ => span_bug!(item.span, "associated_item_def_ids: not impl or trait"),
}
}
fn associated_items(tcx: TyCtxt<'_>, def_id: DefId) -> ty::AssocItems {
if tcx.is_trait_alias(def_id) {
ty::AssocItems::new(Vec::new())
} else {
let items = tcx.associated_item_def_ids(def_id).iter().map(|did| tcx.associated_item(*did));
ty::AssocItems::new(items)
}
}
fn impl_item_implementor_ids(tcx: TyCtxt<'_>, impl_id: DefId) -> DefIdMap<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()
}
fn associated_item(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::AssocItem {
let id = tcx.local_def_id_to_hir_id(def_id);
let parent_def_id = tcx.hir().get_parent_item(id);
let parent_item = tcx.hir().expect_item(parent_def_id.def_id);
match parent_item.kind {
hir::ItemKind::Impl(impl_) => {
if let Some(impl_item_ref) = impl_.items.iter().find(|i| i.id.owner_id.def_id == def_id)
{
let assoc_item = associated_item_from_impl_item_ref(impl_item_ref);
debug_assert_eq!(assoc_item.def_id.expect_local(), def_id);
return assoc_item;
}
}
hir::ItemKind::Trait(.., trait_item_refs) => {
if let Some(trait_item_ref) =
trait_item_refs.iter().find(|i| i.id.owner_id.def_id == def_id)
{
let assoc_item = associated_item_from_trait_item_ref(trait_item_ref);
debug_assert_eq!(assoc_item.def_id.expect_local(), def_id);
return assoc_item;
}
}
_ => {}
}
span_bug!(
parent_item.span,
"unexpected parent of trait or impl item or item not found: {:?}",
parent_item.kind
)
}
fn associated_item_from_trait_item_ref(trait_item_ref: &hir::TraitItemRef) -> ty::AssocItem {
let owner_id = trait_item_ref.id.owner_id;
let (kind, has_self) = match trait_item_ref.kind {
hir::AssocItemKind::Const => (ty::AssocKind::Const, false),
hir::AssocItemKind::Fn { has_self } => (ty::AssocKind::Fn, has_self),
hir::AssocItemKind::Type => (ty::AssocKind::Type, false),
};
ty::AssocItem {
name: trait_item_ref.ident.name,
kind,
def_id: owner_id.to_def_id(),
trait_item_def_id: Some(owner_id.to_def_id()),
container: ty::TraitContainer,
fn_has_self_parameter: has_self,
opt_rpitit_info: None,
}
}
fn associated_item_from_impl_item_ref(impl_item_ref: &hir::ImplItemRef) -> ty::AssocItem {
let def_id = impl_item_ref.id.owner_id;
let (kind, has_self) = match impl_item_ref.kind {
hir::AssocItemKind::Const => (ty::AssocKind::Const, false),
hir::AssocItemKind::Fn { has_self } => (ty::AssocKind::Fn, has_self),
hir::AssocItemKind::Type => (ty::AssocKind::Type, false),
};
ty::AssocItem {
name: impl_item_ref.ident.name,
kind,
def_id: def_id.to_def_id(),
trait_item_def_id: impl_item_ref.trait_item_def_id,
container: ty::ImplContainer,
fn_has_self_parameter: has_self,
opt_rpitit_info: None,
}
}
fn associated_types_for_impl_traits_in_associated_fn(
tcx: TyCtxt<'_>,
fn_def_id: LocalDefId,
) -> &'_ [DefId] {
let parent_def_id = tcx.local_parent(fn_def_id);
match tcx.def_kind(parent_def_id) {
DefKind::Trait => {
struct RPITVisitor<'tcx> {
rpits: FxIndexSet<LocalDefId>,
tcx: TyCtxt<'tcx>,
}
impl<'tcx> Visitor<'tcx> for RPITVisitor<'tcx> {
fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) {
if let hir::TyKind::OpaqueDef(item_id, _, _) = ty.kind
&& self.rpits.insert(item_id.owner_id.def_id)
{
let opaque_item =
self.tcx.hir().expect_item(item_id.owner_id.def_id).expect_opaque_ty();
for bound in opaque_item.bounds {
intravisit::walk_param_bound(self, bound);
}
}
intravisit::walk_ty(self, ty)
}
}
let mut visitor = RPITVisitor { tcx, rpits: FxIndexSet::default() };
if let Some(output) = tcx.hir().get_fn_output(fn_def_id) {
visitor.visit_fn_ret_ty(output);
tcx.arena.alloc_from_iter(visitor.rpits.iter().map(|opaque_ty_def_id| {
tcx.associated_type_for_impl_trait_in_trait(opaque_ty_def_id).to_def_id()
}))
} else {
&[]
}
}
DefKind::Impl { .. } => {
let Some(trait_fn_def_id) = tcx.associated_item(fn_def_id).trait_item_def_id else {
return &[];
};
tcx.arena.alloc_from_iter(
tcx.associated_types_for_impl_traits_in_associated_fn(trait_fn_def_id).iter().map(
move |&trait_assoc_def_id| {
associated_type_for_impl_trait_in_impl(tcx, trait_assoc_def_id, fn_def_id)
.to_def_id()
},
),
)
}
def_kind => bug!(
"associated_types_for_impl_traits_in_associated_fn: {:?} should be Trait or Impl but is {:?}",
parent_def_id,
def_kind
),
}
}
fn feed_hir(feed: &TyCtxtFeed<'_, LocalDefId>) {
feed.local_def_id_to_hir_id(HirId::make_owner(feed.def_id()));
let node = hir::OwnerNode::AssocOpaqueTy(&hir::AssocOpaqueTy {});
let bodies = Default::default();
let attrs = hir::AttributeMap::EMPTY;
let (opt_hash_including_bodies, _) = feed.tcx.hash_owner_nodes(node, &bodies, &attrs.map);
feed.opt_hir_owner_nodes(Some(feed.tcx.arena.alloc(hir::OwnerNodes {
opt_hash_including_bodies,
nodes: IndexVec::from_elem_n(
hir::ParentedNode {
parent: hir::ItemLocalId::INVALID,
node: hir::Node::AssocOpaqueTy(&hir::AssocOpaqueTy {}),
},
1,
),
bodies,
})));
feed.feed_owner_id().hir_attrs(attrs);
}
fn associated_type_for_impl_trait_in_trait(
tcx: TyCtxt<'_>,
opaque_ty_def_id: LocalDefId,
) -> LocalDefId {
let (hir::OpaqueTyOrigin::FnReturn(fn_def_id) | hir::OpaqueTyOrigin::AsyncFn(fn_def_id)) =
tcx.opaque_type_origin(opaque_ty_def_id)
else {
bug!("expected opaque for {opaque_ty_def_id:?}");
};
let trait_def_id = tcx.local_parent(fn_def_id);
assert_eq!(tcx.def_kind(trait_def_id), DefKind::Trait);
let span = tcx.def_span(opaque_ty_def_id);
let trait_assoc_ty = tcx.at(span).create_def(trait_def_id, kw::Empty, DefKind::AssocTy);
let local_def_id = trait_assoc_ty.def_id();
let def_id = local_def_id.to_def_id();
feed_hir(&trait_assoc_ty);
trait_assoc_ty.def_ident_span(Some(span));
trait_assoc_ty.associated_item(ty::AssocItem {
name: kw::Empty,
kind: ty::AssocKind::Type,
def_id,
trait_item_def_id: None,
container: ty::TraitContainer,
fn_has_self_parameter: false,
opt_rpitit_info: Some(ImplTraitInTraitData::Trait {
fn_def_id: fn_def_id.to_def_id(),
opaque_def_id: opaque_ty_def_id.to_def_id(),
}),
});
trait_assoc_ty.visibility(tcx.visibility(fn_def_id));
trait_assoc_ty.defaultness(tcx.defaultness(fn_def_id));
trait_assoc_ty.is_type_alias_impl_trait(false);
trait_assoc_ty.inferred_outlives_of(&[]);
local_def_id
}
fn associated_type_for_impl_trait_in_impl(
tcx: TyCtxt<'_>,
trait_assoc_def_id: DefId,
impl_fn_def_id: LocalDefId,
) -> LocalDefId {
let impl_local_def_id = tcx.local_parent(impl_fn_def_id);
let decl = tcx.hir_node_by_def_id(impl_fn_def_id).fn_decl().expect("expected decl");
let span = match decl.output {
hir::FnRetTy::DefaultReturn(_) => tcx.def_span(impl_fn_def_id),
hir::FnRetTy::Return(ty) => ty.span,
};
let impl_assoc_ty = tcx.at(span).create_def(impl_local_def_id, kw::Empty, DefKind::AssocTy);
let local_def_id = impl_assoc_ty.def_id();
let def_id = local_def_id.to_def_id();
feed_hir(&impl_assoc_ty);
impl_assoc_ty.def_ident_span(Some(span));
impl_assoc_ty.associated_item(ty::AssocItem {
name: kw::Empty,
kind: ty::AssocKind::Type,
def_id,
trait_item_def_id: Some(trait_assoc_def_id),
container: ty::ImplContainer,
fn_has_self_parameter: false,
opt_rpitit_info: Some(ImplTraitInTraitData::Impl { fn_def_id: impl_fn_def_id.to_def_id() }),
});
impl_assoc_ty.visibility(tcx.visibility(impl_fn_def_id));
impl_assoc_ty.defaultness(tcx.defaultness(impl_fn_def_id));
impl_assoc_ty.generics_of({
let trait_assoc_generics = tcx.generics_of(trait_assoc_def_id);
let trait_assoc_parent_count = trait_assoc_generics.parent_count;
let mut params = trait_assoc_generics.params.clone();
let parent_generics = tcx.generics_of(impl_local_def_id.to_def_id());
let parent_count = parent_generics.parent_count + parent_generics.params.len();
for param in &mut params {
param.index = param.index + parent_count as u32 - trait_assoc_parent_count as u32;
}
let param_def_id_to_index =
params.iter().map(|param| (param.def_id, param.index)).collect();
ty::Generics {
parent: Some(impl_local_def_id.to_def_id()),
parent_count,
params,
param_def_id_to_index,
has_self: false,
has_late_bound_regions: trait_assoc_generics.has_late_bound_regions,
host_effect_index: parent_generics.host_effect_index,
}
});
impl_assoc_ty.inferred_outlives_of(&[]);
local_def_id
}