rustc_ty_utils/
assoc.rs

1use rustc_hir::def::DefKind;
2use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId};
3use rustc_hir::definitions::{DefPathData, DisambiguatorState};
4use rustc_hir::intravisit::{self, Visitor};
5use rustc_hir::{self as hir, ItemKind};
6use rustc_middle::query::Providers;
7use rustc_middle::ty::{self, ImplTraitInTraitData, TyCtxt};
8use rustc_middle::{bug, span_bug};
9use rustc_span::Ident;
10use rustc_span::symbol::kw;
11
12pub(crate) fn provide(providers: &mut Providers) {
13    *providers = Providers {
14        associated_item,
15        associated_item_def_ids,
16        associated_items,
17        associated_types_for_impl_traits_in_trait_or_impl,
18        impl_item_implementor_ids,
19        ..*providers
20    };
21}
22
23fn associated_item_def_ids(tcx: TyCtxt<'_>, def_id: LocalDefId) -> &[DefId] {
24    let item = tcx.hir_expect_item(def_id);
25    match item.kind {
26        hir::ItemKind::Trait(.., trait_item_refs) => {
27            // We collect RPITITs for each trait method's return type and create a corresponding
28            // associated item using the associated_types_for_impl_traits_in_trait_or_impl
29            // query.
30            let rpitit_items = tcx.associated_types_for_impl_traits_in_trait_or_impl(def_id);
31            tcx.arena.alloc_from_iter(trait_item_refs.iter().flat_map(|trait_item_ref| {
32                let item_def_id = trait_item_ref.owner_id.to_def_id();
33                [item_def_id]
34                    .into_iter()
35                    .chain(rpitit_items.get(&item_def_id).into_iter().flatten().copied())
36            }))
37        }
38        hir::ItemKind::Impl(impl_) => {
39            // We collect RPITITs for each trait method's return type, on the impl side too and
40            // create a corresponding associated item using
41            // associated_types_for_impl_traits_in_trait_or_impl query.
42            let rpitit_items = tcx.associated_types_for_impl_traits_in_trait_or_impl(def_id);
43            tcx.arena.alloc_from_iter(impl_.items.iter().flat_map(|impl_item_ref| {
44                let item_def_id = impl_item_ref.owner_id.to_def_id();
45                [item_def_id]
46                    .into_iter()
47                    .chain(rpitit_items.get(&item_def_id).into_iter().flatten().copied())
48            }))
49        }
50        _ => span_bug!(item.span, "associated_item_def_ids: not impl or trait"),
51    }
52}
53
54fn associated_items(tcx: TyCtxt<'_>, def_id: DefId) -> ty::AssocItems {
55    if tcx.is_trait_alias(def_id) {
56        ty::AssocItems::new(Vec::new())
57    } else {
58        let items = tcx.associated_item_def_ids(def_id).iter().map(|did| tcx.associated_item(*did));
59        ty::AssocItems::new(items)
60    }
61}
62
63fn impl_item_implementor_ids(tcx: TyCtxt<'_>, impl_id: DefId) -> DefIdMap<DefId> {
64    tcx.associated_items(impl_id)
65        .in_definition_order()
66        .filter_map(|item| item.trait_item_def_id.map(|trait_item| (trait_item, item.def_id)))
67        .collect()
68}
69
70fn associated_item(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::AssocItem {
71    let assoc_item = match tcx.hir_node_by_def_id(def_id) {
72        hir::Node::TraitItem(ti) => associated_item_from_trait_item(tcx, ti),
73        hir::Node::ImplItem(ii) => associated_item_from_impl_item(tcx, ii),
74        node => span_bug!(tcx.def_span(def_id), "impl item or item not found: {:?}", node,),
75    };
76    debug_assert_eq!(assoc_item.def_id.expect_local(), def_id);
77    assoc_item
78}
79
80fn fn_has_self_parameter(tcx: TyCtxt<'_>, owner_id: hir::OwnerId) -> bool {
81    matches!(tcx.fn_arg_idents(owner_id.def_id), [Some(Ident { name: kw::SelfLower, .. }), ..])
82}
83
84fn associated_item_from_trait_item(
85    tcx: TyCtxt<'_>,
86    trait_item: &hir::TraitItem<'_>,
87) -> ty::AssocItem {
88    let owner_id = trait_item.owner_id;
89    let name = trait_item.ident.name;
90    let kind = match trait_item.kind {
91        hir::TraitItemKind::Const { .. } => ty::AssocKind::Const { name },
92        hir::TraitItemKind::Fn { .. } => {
93            ty::AssocKind::Fn { name, has_self: fn_has_self_parameter(tcx, owner_id) }
94        }
95        hir::TraitItemKind::Type { .. } => {
96            ty::AssocKind::Type { data: ty::AssocTypeData::Normal(name) }
97        }
98    };
99
100    ty::AssocItem {
101        kind,
102        def_id: owner_id.to_def_id(),
103        trait_item_def_id: Some(owner_id.to_def_id()),
104        container: ty::AssocItemContainer::Trait,
105    }
106}
107
108fn associated_item_from_impl_item(tcx: TyCtxt<'_>, impl_item: &hir::ImplItem<'_>) -> ty::AssocItem {
109    let owner_id = impl_item.owner_id;
110    let name = impl_item.ident.name;
111    let kind = match impl_item.kind {
112        hir::ImplItemKind::Const { .. } => ty::AssocKind::Const { name },
113        hir::ImplItemKind::Fn { .. } => {
114            ty::AssocKind::Fn { name, has_self: fn_has_self_parameter(tcx, owner_id) }
115        }
116        hir::ImplItemKind::Type { .. } => {
117            ty::AssocKind::Type { data: ty::AssocTypeData::Normal(name) }
118        }
119    };
120
121    ty::AssocItem {
122        kind,
123        def_id: owner_id.to_def_id(),
124        trait_item_def_id: impl_item.trait_item_def_id,
125        container: ty::AssocItemContainer::Impl,
126    }
127}
128struct RPITVisitor<'a, 'tcx> {
129    tcx: TyCtxt<'tcx>,
130    synthetics: Vec<LocalDefId>,
131    data: DefPathData,
132    disambiguator: &'a mut DisambiguatorState,
133}
134
135impl<'tcx> Visitor<'tcx> for RPITVisitor<'_, 'tcx> {
136    fn visit_opaque_ty(&mut self, opaque: &'tcx hir::OpaqueTy<'tcx>) -> Self::Result {
137        self.synthetics.push(associated_type_for_impl_trait_in_trait(
138            self.tcx,
139            opaque.def_id,
140            self.data,
141            &mut self.disambiguator,
142        ));
143        intravisit::walk_opaque_ty(self, opaque)
144    }
145}
146
147fn associated_types_for_impl_traits_in_trait_or_impl<'tcx>(
148    tcx: TyCtxt<'tcx>,
149    def_id: LocalDefId,
150) -> DefIdMap<Vec<DefId>> {
151    let item = tcx.hir_expect_item(def_id);
152    let disambiguator = &mut DisambiguatorState::new();
153    match item.kind {
154        ItemKind::Trait(.., trait_item_refs) => trait_item_refs
155            .iter()
156            .filter_map(move |item| {
157                if !matches!(tcx.def_kind(item.owner_id), DefKind::AssocFn) {
158                    return None;
159                }
160                let fn_def_id = item.owner_id.def_id;
161                let Some(output) = tcx.hir_get_fn_output(fn_def_id) else {
162                    return Some((fn_def_id.to_def_id(), vec![]));
163                };
164                let def_name = tcx.item_name(fn_def_id.to_def_id());
165                let data = DefPathData::AnonAssocTy(def_name);
166                let mut visitor = RPITVisitor { tcx, synthetics: vec![], data, disambiguator };
167                visitor.visit_fn_ret_ty(output);
168                let defs = visitor
169                    .synthetics
170                    .into_iter()
171                    .map(|def_id| def_id.to_def_id())
172                    .collect::<Vec<_>>();
173                Some((fn_def_id.to_def_id(), defs))
174            })
175            .collect(),
176        ItemKind::Impl(impl_) => {
177            let Some(of_trait) = impl_.of_trait else {
178                return Default::default();
179            };
180            let Some(trait_def_id) = of_trait.trait_ref.trait_def_id() else {
181                return Default::default();
182            };
183            let in_trait_def = tcx.associated_types_for_impl_traits_in_trait_or_impl(trait_def_id);
184            impl_
185                .items
186                .iter()
187                .filter_map(|item| {
188                    if !matches!(tcx.def_kind(item.owner_id), DefKind::AssocFn) {
189                        return None;
190                    }
191                    let did = item.owner_id.def_id.to_def_id();
192                    let item = tcx.hir_impl_item(*item);
193                    let Some(trait_item_def_id) = item.trait_item_def_id else {
194                        return Some((did, vec![]));
195                    };
196                    let iter = in_trait_def[&trait_item_def_id].iter().map(|&id| {
197                        associated_type_for_impl_trait_in_impl(tcx, id, item, disambiguator)
198                            .to_def_id()
199                    });
200                    Some((did, iter.collect()))
201                })
202                .collect()
203        }
204        _ => {
205            bug!(
206                "associated_types_for_impl_traits_in_trait_or_impl: {:?} should be Trait or Impl but is {:?}",
207                def_id,
208                tcx.def_kind(def_id)
209            )
210        }
211    }
212}
213
214/// Given an `opaque_ty_def_id` corresponding to an `impl Trait` in an associated
215/// function from a trait, synthesize an associated type for that `impl Trait`
216/// that inherits properties that we infer from the method and the opaque type.
217fn associated_type_for_impl_trait_in_trait(
218    tcx: TyCtxt<'_>,
219    opaque_ty_def_id: LocalDefId,
220    data: DefPathData,
221    disambiguator: &mut DisambiguatorState,
222) -> LocalDefId {
223    let (hir::OpaqueTyOrigin::FnReturn { parent: fn_def_id, .. }
224    | hir::OpaqueTyOrigin::AsyncFn { parent: fn_def_id, .. }) =
225        tcx.local_opaque_ty_origin(opaque_ty_def_id)
226    else {
227        bug!("expected opaque for {opaque_ty_def_id:?}");
228    };
229    let trait_def_id = tcx.local_parent(fn_def_id);
230    assert_eq!(tcx.def_kind(trait_def_id), DefKind::Trait);
231
232    let span = tcx.def_span(opaque_ty_def_id);
233    // Also use the method name to create an unique def path.
234    let trait_assoc_ty = tcx.at(span).create_def(
235        trait_def_id,
236        // No name because this is an anonymous associated type.
237        None,
238        DefKind::AssocTy,
239        Some(data),
240        disambiguator,
241    );
242
243    let local_def_id = trait_assoc_ty.def_id();
244    let def_id = local_def_id.to_def_id();
245
246    trait_assoc_ty.feed_hir();
247
248    // Copy span of the opaque.
249    trait_assoc_ty.def_ident_span(Some(span));
250
251    trait_assoc_ty.associated_item(ty::AssocItem {
252        kind: ty::AssocKind::Type {
253            data: ty::AssocTypeData::Rpitit(ImplTraitInTraitData::Trait {
254                fn_def_id: fn_def_id.to_def_id(),
255                opaque_def_id: opaque_ty_def_id.to_def_id(),
256            }),
257        },
258        def_id,
259        trait_item_def_id: None,
260        container: ty::AssocItemContainer::Trait,
261    });
262
263    // Copy visility of the containing function.
264    trait_assoc_ty.visibility(tcx.visibility(fn_def_id));
265
266    // Copy defaultness of the containing function.
267    trait_assoc_ty.defaultness(tcx.defaultness(fn_def_id));
268
269    // There are no inferred outlives for the synthesized associated type.
270    trait_assoc_ty.inferred_outlives_of(&[]);
271
272    local_def_id
273}
274
275/// Given an `trait_assoc_def_id` corresponding to an associated item synthesized
276/// from an `impl Trait` in an associated function from a trait, and an
277/// `impl_fn` that represents an implementation of the associated function
278/// that the `impl Trait` comes from, synthesize an associated type for that `impl Trait`
279/// that inherits properties that we infer from the method and the associated type.
280fn associated_type_for_impl_trait_in_impl(
281    tcx: TyCtxt<'_>,
282    trait_assoc_def_id: DefId,
283    impl_fn: &hir::ImplItem<'_>,
284    disambiguator: &mut DisambiguatorState,
285) -> LocalDefId {
286    let impl_local_def_id = tcx.local_parent(impl_fn.owner_id.def_id);
287
288    let hir::ImplItemKind::Fn(fn_sig, _) = impl_fn.kind else { bug!("expected decl") };
289    let span = match fn_sig.decl.output {
290        hir::FnRetTy::DefaultReturn(_) => tcx.def_span(impl_fn.owner_id),
291        hir::FnRetTy::Return(ty) => ty.span,
292    };
293
294    // Use the same disambiguator and method name as the anon associated type in the trait.
295    let disambiguated_data = tcx.def_key(trait_assoc_def_id).disambiguated_data;
296    let DefPathData::AnonAssocTy(name) = disambiguated_data.data else {
297        bug!("expected anon associated type")
298    };
299    let data = DefPathData::AnonAssocTy(name);
300
301    let impl_assoc_ty = tcx.at(span).create_def(
302        impl_local_def_id,
303        // No name because this is an anonymous associated type.
304        None,
305        DefKind::AssocTy,
306        Some(data),
307        disambiguator,
308    );
309
310    let local_def_id = impl_assoc_ty.def_id();
311    let def_id = local_def_id.to_def_id();
312
313    impl_assoc_ty.feed_hir();
314
315    // Copy span of the opaque.
316    impl_assoc_ty.def_ident_span(Some(span));
317
318    impl_assoc_ty.associated_item(ty::AssocItem {
319        kind: ty::AssocKind::Type {
320            data: ty::AssocTypeData::Rpitit(ImplTraitInTraitData::Impl {
321                fn_def_id: impl_fn.owner_id.to_def_id(),
322            }),
323        },
324        def_id,
325        trait_item_def_id: Some(trait_assoc_def_id),
326        container: ty::AssocItemContainer::Impl,
327    });
328
329    // Copy visility of the containing function.
330    impl_assoc_ty.visibility(tcx.visibility(impl_fn.owner_id));
331
332    // Copy defaultness of the containing function.
333    impl_assoc_ty.defaultness(tcx.defaultness(impl_fn.owner_id));
334
335    // Copy generics_of the trait's associated item but the impl as the parent.
336    // FIXME: This may be detrimental to diagnostics, as we resolve the early-bound vars
337    // here to paramswhose parent are items in the trait. We could synthesize new params
338    // here, but it seems overkill.
339    impl_assoc_ty.generics_of({
340        let trait_assoc_generics = tcx.generics_of(trait_assoc_def_id);
341        let trait_assoc_parent_count = trait_assoc_generics.parent_count;
342        let mut own_params = trait_assoc_generics.own_params.clone();
343
344        let parent_generics = tcx.generics_of(impl_local_def_id.to_def_id());
345        let parent_count = parent_generics.parent_count + parent_generics.own_params.len();
346
347        for param in &mut own_params {
348            param.index = param.index + parent_count as u32 - trait_assoc_parent_count as u32;
349        }
350
351        let param_def_id_to_index =
352            own_params.iter().map(|param| (param.def_id, param.index)).collect();
353
354        ty::Generics {
355            parent: Some(impl_local_def_id.to_def_id()),
356            parent_count,
357            own_params,
358            param_def_id_to_index,
359            has_self: false,
360            has_late_bound_regions: trait_assoc_generics.has_late_bound_regions,
361        }
362    });
363
364    // There are no inferred outlives for the synthesized associated type.
365    impl_assoc_ty.inferred_outlives_of(&[]);
366
367    local_def_id
368}