rustc_ty_utils/
assoc.rs

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