rustc_ty_utils/
assoc.rs

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