mirror of
https://github.com/rust-lang/rust.git
synced 2025-12-03 08:29:56 +00:00
369 lines
14 KiB
Rust
369 lines
14 KiB
Rust
use rustc_hir::def::DefKind;
|
|
use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId};
|
|
use rustc_hir::definitions::{DefPathData, DisambiguatorState};
|
|
use rustc_hir::intravisit::{self, Visitor};
|
|
use rustc_hir::{self as hir, ItemKind};
|
|
use rustc_middle::query::Providers;
|
|
use rustc_middle::ty::{self, ImplTraitInTraitData, TyCtxt};
|
|
use rustc_middle::{bug, span_bug};
|
|
use rustc_span::Ident;
|
|
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_trait_or_impl,
|
|
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) => {
|
|
// We collect RPITITs for each trait method's return type and create a corresponding
|
|
// associated item using the associated_types_for_impl_traits_in_trait_or_impl
|
|
// query.
|
|
let rpitit_items = tcx.associated_types_for_impl_traits_in_trait_or_impl(def_id);
|
|
tcx.arena.alloc_from_iter(trait_item_refs.iter().flat_map(|trait_item_ref| {
|
|
let item_def_id = trait_item_ref.owner_id.to_def_id();
|
|
[item_def_id]
|
|
.into_iter()
|
|
.chain(rpitit_items.get(&item_def_id).into_iter().flatten().copied())
|
|
}))
|
|
}
|
|
hir::ItemKind::Impl(impl_) => {
|
|
// We collect RPITITs for each trait method's return type, on the impl side too and
|
|
// create a corresponding associated item using
|
|
// associated_types_for_impl_traits_in_trait_or_impl query.
|
|
let rpitit_items = tcx.associated_types_for_impl_traits_in_trait_or_impl(def_id);
|
|
tcx.arena.alloc_from_iter(impl_.items.iter().flat_map(|impl_item_ref| {
|
|
let item_def_id = impl_item_ref.owner_id.to_def_id();
|
|
[item_def_id]
|
|
.into_iter()
|
|
.chain(rpitit_items.get(&item_def_id).into_iter().flatten().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 assoc_item = match tcx.hir_node_by_def_id(def_id) {
|
|
hir::Node::TraitItem(ti) => associated_item_from_trait_item(tcx, ti),
|
|
hir::Node::ImplItem(ii) => associated_item_from_impl_item(tcx, ii),
|
|
node => span_bug!(tcx.def_span(def_id), "impl item or item not found: {:?}", node,),
|
|
};
|
|
debug_assert_eq!(assoc_item.def_id.expect_local(), def_id);
|
|
assoc_item
|
|
}
|
|
|
|
fn fn_has_self_parameter(tcx: TyCtxt<'_>, owner_id: hir::OwnerId) -> bool {
|
|
matches!(tcx.fn_arg_idents(owner_id.def_id), [Some(Ident { name: kw::SelfLower, .. }), ..])
|
|
}
|
|
|
|
fn associated_item_from_trait_item(
|
|
tcx: TyCtxt<'_>,
|
|
trait_item: &hir::TraitItem<'_>,
|
|
) -> ty::AssocItem {
|
|
let owner_id = trait_item.owner_id;
|
|
let name = trait_item.ident.name;
|
|
let kind = match trait_item.kind {
|
|
hir::TraitItemKind::Const { .. } => ty::AssocKind::Const { name },
|
|
hir::TraitItemKind::Fn { .. } => {
|
|
ty::AssocKind::Fn { name, has_self: fn_has_self_parameter(tcx, owner_id) }
|
|
}
|
|
hir::TraitItemKind::Type { .. } => {
|
|
ty::AssocKind::Type { data: ty::AssocTypeData::Normal(name) }
|
|
}
|
|
};
|
|
|
|
ty::AssocItem {
|
|
kind,
|
|
def_id: owner_id.to_def_id(),
|
|
trait_item_def_id: Some(owner_id.to_def_id()),
|
|
container: ty::AssocItemContainer::Trait,
|
|
}
|
|
}
|
|
|
|
fn associated_item_from_impl_item(tcx: TyCtxt<'_>, impl_item: &hir::ImplItem<'_>) -> ty::AssocItem {
|
|
let owner_id = impl_item.owner_id;
|
|
let name = impl_item.ident.name;
|
|
let kind = match impl_item.kind {
|
|
hir::ImplItemKind::Const { .. } => ty::AssocKind::Const { name },
|
|
hir::ImplItemKind::Fn { .. } => {
|
|
ty::AssocKind::Fn { name, has_self: fn_has_self_parameter(tcx, owner_id) }
|
|
}
|
|
hir::ImplItemKind::Type { .. } => {
|
|
ty::AssocKind::Type { data: ty::AssocTypeData::Normal(name) }
|
|
}
|
|
};
|
|
|
|
ty::AssocItem {
|
|
kind,
|
|
def_id: owner_id.to_def_id(),
|
|
trait_item_def_id: impl_item.trait_item_def_id,
|
|
container: ty::AssocItemContainer::Impl,
|
|
}
|
|
}
|
|
struct RPITVisitor<'a, 'tcx> {
|
|
tcx: TyCtxt<'tcx>,
|
|
synthetics: Vec<LocalDefId>,
|
|
data: DefPathData,
|
|
disambiguator: &'a mut DisambiguatorState,
|
|
}
|
|
|
|
impl<'tcx> Visitor<'tcx> for RPITVisitor<'_, 'tcx> {
|
|
fn visit_opaque_ty(&mut self, opaque: &'tcx hir::OpaqueTy<'tcx>) -> Self::Result {
|
|
self.synthetics.push(associated_type_for_impl_trait_in_trait(
|
|
self.tcx,
|
|
opaque.def_id,
|
|
self.data,
|
|
&mut self.disambiguator,
|
|
));
|
|
intravisit::walk_opaque_ty(self, opaque)
|
|
}
|
|
}
|
|
|
|
fn associated_types_for_impl_traits_in_trait_or_impl<'tcx>(
|
|
tcx: TyCtxt<'tcx>,
|
|
def_id: LocalDefId,
|
|
) -> DefIdMap<Vec<DefId>> {
|
|
let item = tcx.hir_expect_item(def_id);
|
|
let disambiguator = &mut DisambiguatorState::new();
|
|
match item.kind {
|
|
ItemKind::Trait(.., trait_item_refs) => trait_item_refs
|
|
.iter()
|
|
.filter_map(move |item| {
|
|
if !matches!(tcx.def_kind(item.owner_id), DefKind::AssocFn) {
|
|
return None;
|
|
}
|
|
let fn_def_id = item.owner_id.def_id;
|
|
let Some(output) = tcx.hir_get_fn_output(fn_def_id) else {
|
|
return Some((fn_def_id.to_def_id(), vec![]));
|
|
};
|
|
let def_name = tcx.item_name(fn_def_id.to_def_id());
|
|
let data = DefPathData::AnonAssocTy(def_name);
|
|
let mut visitor = RPITVisitor { tcx, synthetics: vec![], data, disambiguator };
|
|
visitor.visit_fn_ret_ty(output);
|
|
let defs = visitor
|
|
.synthetics
|
|
.into_iter()
|
|
.map(|def_id| def_id.to_def_id())
|
|
.collect::<Vec<_>>();
|
|
Some((fn_def_id.to_def_id(), defs))
|
|
})
|
|
.collect(),
|
|
ItemKind::Impl(impl_) => {
|
|
let Some(trait_ref) = impl_.of_trait else {
|
|
return Default::default();
|
|
};
|
|
let Some(trait_def_id) = trait_ref.trait_def_id() else {
|
|
return Default::default();
|
|
};
|
|
let in_trait_def = tcx.associated_types_for_impl_traits_in_trait_or_impl(trait_def_id);
|
|
impl_
|
|
.items
|
|
.iter()
|
|
.filter_map(|item| {
|
|
if !matches!(tcx.def_kind(item.owner_id), DefKind::AssocFn) {
|
|
return None;
|
|
}
|
|
let did = item.owner_id.def_id.to_def_id();
|
|
let item = tcx.hir_impl_item(*item);
|
|
let Some(trait_item_def_id) = item.trait_item_def_id else {
|
|
return Some((did, vec![]));
|
|
};
|
|
let iter = in_trait_def[&trait_item_def_id].iter().map(|&id| {
|
|
associated_type_for_impl_trait_in_impl(tcx, id, item, disambiguator)
|
|
.to_def_id()
|
|
});
|
|
Some((did, iter.collect()))
|
|
})
|
|
.collect()
|
|
}
|
|
_ => {
|
|
bug!(
|
|
"associated_types_for_impl_traits_in_trait_or_impl: {:?} should be Trait or Impl but is {:?}",
|
|
def_id,
|
|
tcx.def_kind(def_id)
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Given an `opaque_ty_def_id` corresponding to an `impl Trait` in an associated
|
|
/// function from a trait, synthesize an associated type for that `impl Trait`
|
|
/// that inherits properties that we infer from the method and the opaque type.
|
|
fn associated_type_for_impl_trait_in_trait(
|
|
tcx: TyCtxt<'_>,
|
|
opaque_ty_def_id: LocalDefId,
|
|
data: DefPathData,
|
|
disambiguator: &mut DisambiguatorState,
|
|
) -> LocalDefId {
|
|
let (hir::OpaqueTyOrigin::FnReturn { parent: fn_def_id, .. }
|
|
| hir::OpaqueTyOrigin::AsyncFn { parent: fn_def_id, .. }) =
|
|
tcx.local_opaque_ty_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);
|
|
// Also use the method name to create an unique def path.
|
|
let trait_assoc_ty = tcx.at(span).create_def(
|
|
trait_def_id,
|
|
// No name because this is an anonymous associated type.
|
|
None,
|
|
DefKind::AssocTy,
|
|
Some(data),
|
|
disambiguator,
|
|
);
|
|
|
|
let local_def_id = trait_assoc_ty.def_id();
|
|
let def_id = local_def_id.to_def_id();
|
|
|
|
trait_assoc_ty.feed_hir();
|
|
|
|
// Copy span of the opaque.
|
|
trait_assoc_ty.def_ident_span(Some(span));
|
|
|
|
trait_assoc_ty.associated_item(ty::AssocItem {
|
|
kind: ty::AssocKind::Type {
|
|
data: ty::AssocTypeData::Rpitit(ImplTraitInTraitData::Trait {
|
|
fn_def_id: fn_def_id.to_def_id(),
|
|
opaque_def_id: opaque_ty_def_id.to_def_id(),
|
|
}),
|
|
},
|
|
def_id,
|
|
trait_item_def_id: None,
|
|
container: ty::AssocItemContainer::Trait,
|
|
});
|
|
|
|
// Copy visility of the containing function.
|
|
trait_assoc_ty.visibility(tcx.visibility(fn_def_id));
|
|
|
|
// Copy defaultness of the containing function.
|
|
trait_assoc_ty.defaultness(tcx.defaultness(fn_def_id));
|
|
|
|
// There are no inferred outlives for the synthesized associated type.
|
|
trait_assoc_ty.inferred_outlives_of(&[]);
|
|
|
|
local_def_id
|
|
}
|
|
|
|
/// Given an `trait_assoc_def_id` corresponding to an associated item synthesized
|
|
/// from an `impl Trait` in an associated function from a trait, and an
|
|
/// `impl_fn` that represents an implementation of the associated function
|
|
/// that the `impl Trait` comes from, synthesize an associated type for that `impl Trait`
|
|
/// that inherits properties that we infer from the method and the associated type.
|
|
fn associated_type_for_impl_trait_in_impl(
|
|
tcx: TyCtxt<'_>,
|
|
trait_assoc_def_id: DefId,
|
|
impl_fn: &hir::ImplItem<'_>,
|
|
disambiguator: &mut DisambiguatorState,
|
|
) -> LocalDefId {
|
|
let impl_local_def_id = tcx.local_parent(impl_fn.owner_id.def_id);
|
|
|
|
let hir::ImplItemKind::Fn(fn_sig, _) = impl_fn.kind else { bug!("expected decl") };
|
|
let span = match fn_sig.decl.output {
|
|
hir::FnRetTy::DefaultReturn(_) => tcx.def_span(impl_fn.owner_id),
|
|
hir::FnRetTy::Return(ty) => ty.span,
|
|
};
|
|
|
|
// Use the same disambiguator and method name as the anon associated type in the trait.
|
|
let disambiguated_data = tcx.def_key(trait_assoc_def_id).disambiguated_data;
|
|
let DefPathData::AnonAssocTy(name) = disambiguated_data.data else {
|
|
bug!("expected anon associated type")
|
|
};
|
|
let data = DefPathData::AnonAssocTy(name);
|
|
|
|
let impl_assoc_ty = tcx.at(span).create_def(
|
|
impl_local_def_id,
|
|
// No name because this is an anonymous associated type.
|
|
None,
|
|
DefKind::AssocTy,
|
|
Some(data),
|
|
disambiguator,
|
|
);
|
|
|
|
let local_def_id = impl_assoc_ty.def_id();
|
|
let def_id = local_def_id.to_def_id();
|
|
|
|
impl_assoc_ty.feed_hir();
|
|
|
|
// Copy span of the opaque.
|
|
impl_assoc_ty.def_ident_span(Some(span));
|
|
|
|
impl_assoc_ty.associated_item(ty::AssocItem {
|
|
kind: ty::AssocKind::Type {
|
|
data: ty::AssocTypeData::Rpitit(ImplTraitInTraitData::Impl {
|
|
fn_def_id: impl_fn.owner_id.to_def_id(),
|
|
}),
|
|
},
|
|
def_id,
|
|
trait_item_def_id: Some(trait_assoc_def_id),
|
|
container: ty::AssocItemContainer::Impl,
|
|
});
|
|
|
|
// Copy visility of the containing function.
|
|
impl_assoc_ty.visibility(tcx.visibility(impl_fn.owner_id));
|
|
|
|
// Copy defaultness of the containing function.
|
|
impl_assoc_ty.defaultness(tcx.defaultness(impl_fn.owner_id));
|
|
|
|
// Copy generics_of the trait's associated item but the impl as the parent.
|
|
// FIXME: This may be detrimental to diagnostics, as we resolve the early-bound vars
|
|
// here to paramswhose parent are items in the trait. We could synthesize new params
|
|
// here, but it seems overkill.
|
|
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 own_params = trait_assoc_generics.own_params.clone();
|
|
|
|
let parent_generics = tcx.generics_of(impl_local_def_id.to_def_id());
|
|
let parent_count = parent_generics.parent_count + parent_generics.own_params.len();
|
|
|
|
for param in &mut own_params {
|
|
param.index = param.index + parent_count as u32 - trait_assoc_parent_count as u32;
|
|
}
|
|
|
|
let param_def_id_to_index =
|
|
own_params.iter().map(|param| (param.def_id, param.index)).collect();
|
|
|
|
ty::Generics {
|
|
parent: Some(impl_local_def_id.to_def_id()),
|
|
parent_count,
|
|
own_params,
|
|
param_def_id_to_index,
|
|
has_self: false,
|
|
has_late_bound_regions: trait_assoc_generics.has_late_bound_regions,
|
|
}
|
|
});
|
|
|
|
// There are no inferred outlives for the synthesized associated type.
|
|
impl_assoc_ty.inferred_outlives_of(&[]);
|
|
|
|
local_def_id
|
|
}
|