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::intravisit::{self, Visitor};
5use rustc_hir::{self as hir, AmbigArg};
6use rustc_middle::query::Providers;
7use rustc_middle::ty::{self, ImplTraitInTraitData, TyCtxt};
8use rustc_middle::{bug, span_bug};
9use rustc_span::kw;
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 (kind, has_self) = match trait_item_ref.kind {
133        hir::AssocItemKind::Const => (ty::AssocKind::Const, false),
134        hir::AssocItemKind::Fn { has_self } => (ty::AssocKind::Fn, has_self),
135        hir::AssocItemKind::Type => (ty::AssocKind::Type, false),
136    };
137
138    ty::AssocItem {
139        name: trait_item_ref.ident.name,
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        fn_has_self_parameter: has_self,
145        opt_rpitit_info: None,
146    }
147}
148
149fn associated_item_from_impl_item_ref(impl_item_ref: &hir::ImplItemRef) -> ty::AssocItem {
150    let def_id = impl_item_ref.id.owner_id;
151    let (kind, has_self) = match impl_item_ref.kind {
152        hir::AssocItemKind::Const => (ty::AssocKind::Const, false),
153        hir::AssocItemKind::Fn { has_self } => (ty::AssocKind::Fn, has_self),
154        hir::AssocItemKind::Type => (ty::AssocKind::Type, false),
155    };
156
157    ty::AssocItem {
158        name: impl_item_ref.ident.name,
159        kind,
160        def_id: def_id.to_def_id(),
161        trait_item_def_id: impl_item_ref.trait_item_def_id,
162        container: ty::AssocItemContainer::Impl,
163        fn_has_self_parameter: has_self,
164        opt_rpitit_info: None,
165    }
166}
167
168/// Given an `fn_def_id` of a trait or a trait implementation:
169///
170/// if `fn_def_id` is a function defined inside a trait, then it synthesizes
171/// a new def id corresponding to a new associated type for each return-
172/// position `impl Trait` in the signature.
173///
174/// if `fn_def_id` is a function inside of an impl, then for each synthetic
175/// associated type generated for the corresponding trait function described
176/// above, synthesize a corresponding associated type in the impl.
177fn associated_types_for_impl_traits_in_associated_fn(
178    tcx: TyCtxt<'_>,
179    fn_def_id: LocalDefId,
180) -> &'_ [DefId] {
181    let parent_def_id = tcx.local_parent(fn_def_id);
182
183    match tcx.def_kind(parent_def_id) {
184        DefKind::Trait => {
185            struct RPITVisitor {
186                rpits: FxIndexSet<LocalDefId>,
187            }
188
189            impl<'tcx> Visitor<'tcx> for RPITVisitor {
190                fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx, AmbigArg>) {
191                    if let hir::TyKind::OpaqueDef(opaq) = ty.kind
192                        && self.rpits.insert(opaq.def_id)
193                    {
194                        for bound in opaq.bounds {
195                            intravisit::walk_param_bound(self, bound);
196                        }
197                    }
198                    intravisit::walk_ty(self, ty)
199                }
200            }
201
202            let mut visitor = RPITVisitor { rpits: FxIndexSet::default() };
203
204            if let Some(output) = tcx.hir().get_fn_output(fn_def_id) {
205                visitor.visit_fn_ret_ty(output);
206
207                tcx.arena.alloc_from_iter(visitor.rpits.iter().map(|opaque_ty_def_id| {
208                    tcx.associated_type_for_impl_trait_in_trait(opaque_ty_def_id).to_def_id()
209                }))
210            } else {
211                &[]
212            }
213        }
214
215        DefKind::Impl { .. } => {
216            let Some(trait_fn_def_id) = tcx.associated_item(fn_def_id).trait_item_def_id else {
217                return &[];
218            };
219
220            tcx.arena.alloc_from_iter(
221                tcx.associated_types_for_impl_traits_in_associated_fn(trait_fn_def_id).iter().map(
222                    move |&trait_assoc_def_id| {
223                        associated_type_for_impl_trait_in_impl(tcx, trait_assoc_def_id, fn_def_id)
224                            .to_def_id()
225                    },
226                ),
227            )
228        }
229
230        def_kind => bug!(
231            "associated_types_for_impl_traits_in_associated_fn: {:?} should be Trait or Impl but is {:?}",
232            parent_def_id,
233            def_kind
234        ),
235    }
236}
237
238/// Given an `opaque_ty_def_id` corresponding to an `impl Trait` in an associated
239/// function from a trait, synthesize an associated type for that `impl Trait`
240/// that inherits properties that we infer from the method and the opaque type.
241fn associated_type_for_impl_trait_in_trait(
242    tcx: TyCtxt<'_>,
243    opaque_ty_def_id: LocalDefId,
244) -> LocalDefId {
245    let (hir::OpaqueTyOrigin::FnReturn { parent: fn_def_id, .. }
246    | hir::OpaqueTyOrigin::AsyncFn { parent: fn_def_id, .. }) =
247        tcx.local_opaque_ty_origin(opaque_ty_def_id)
248    else {
249        bug!("expected opaque for {opaque_ty_def_id:?}");
250    };
251    let trait_def_id = tcx.local_parent(fn_def_id);
252    assert_eq!(tcx.def_kind(trait_def_id), DefKind::Trait);
253
254    let span = tcx.def_span(opaque_ty_def_id);
255    let trait_assoc_ty = tcx.at(span).create_def(trait_def_id, kw::Empty, DefKind::AssocTy);
256
257    let local_def_id = trait_assoc_ty.def_id();
258    let def_id = local_def_id.to_def_id();
259
260    trait_assoc_ty.feed_hir();
261
262    // Copy span of the opaque.
263    trait_assoc_ty.def_ident_span(Some(span));
264
265    trait_assoc_ty.associated_item(ty::AssocItem {
266        name: kw::Empty,
267        kind: ty::AssocKind::Type,
268        def_id,
269        trait_item_def_id: None,
270        container: ty::AssocItemContainer::Trait,
271        fn_has_self_parameter: false,
272        opt_rpitit_info: Some(ImplTraitInTraitData::Trait {
273            fn_def_id: fn_def_id.to_def_id(),
274            opaque_def_id: opaque_ty_def_id.to_def_id(),
275        }),
276    });
277
278    // Copy visility of the containing function.
279    trait_assoc_ty.visibility(tcx.visibility(fn_def_id));
280
281    // Copy defaultness of the containing function.
282    trait_assoc_ty.defaultness(tcx.defaultness(fn_def_id));
283
284    // There are no inferred outlives for the synthesized associated type.
285    trait_assoc_ty.inferred_outlives_of(&[]);
286
287    local_def_id
288}
289
290/// Given an `trait_assoc_def_id` corresponding to an associated item synthesized
291/// from an `impl Trait` in an associated function from a trait, and an
292/// `impl_fn_def_id` that represents an implementation of the associated function
293/// that the `impl Trait` comes from, synthesize an associated type for that `impl Trait`
294/// that inherits properties that we infer from the method and the associated type.
295fn associated_type_for_impl_trait_in_impl(
296    tcx: TyCtxt<'_>,
297    trait_assoc_def_id: DefId,
298    impl_fn_def_id: LocalDefId,
299) -> LocalDefId {
300    let impl_local_def_id = tcx.local_parent(impl_fn_def_id);
301
302    let decl = tcx.hir_node_by_def_id(impl_fn_def_id).fn_decl().expect("expected decl");
303    let span = match decl.output {
304        hir::FnRetTy::DefaultReturn(_) => tcx.def_span(impl_fn_def_id),
305        hir::FnRetTy::Return(ty) => ty.span,
306    };
307    let impl_assoc_ty = tcx.at(span).create_def(impl_local_def_id, kw::Empty, DefKind::AssocTy);
308
309    let local_def_id = impl_assoc_ty.def_id();
310    let def_id = local_def_id.to_def_id();
311
312    impl_assoc_ty.feed_hir();
313
314    // Copy span of the opaque.
315    impl_assoc_ty.def_ident_span(Some(span));
316
317    impl_assoc_ty.associated_item(ty::AssocItem {
318        name: kw::Empty,
319        kind: ty::AssocKind::Type,
320        def_id,
321        trait_item_def_id: Some(trait_assoc_def_id),
322        container: ty::AssocItemContainer::Impl,
323        fn_has_self_parameter: false,
324        opt_rpitit_info: Some(ImplTraitInTraitData::Impl { fn_def_id: impl_fn_def_id.to_def_id() }),
325    });
326
327    // Copy visility of the containing function.
328    impl_assoc_ty.visibility(tcx.visibility(impl_fn_def_id));
329
330    // Copy defaultness of the containing function.
331    impl_assoc_ty.defaultness(tcx.defaultness(impl_fn_def_id));
332
333    // Copy generics_of the trait's associated item but the impl as the parent.
334    // FIXME: This may be detrimental to diagnostics, as we resolve the early-bound vars
335    // here to paramswhose parent are items in the trait. We could synthesize new params
336    // here, but it seems overkill.
337    impl_assoc_ty.generics_of({
338        let trait_assoc_generics = tcx.generics_of(trait_assoc_def_id);
339        let trait_assoc_parent_count = trait_assoc_generics.parent_count;
340        let mut own_params = trait_assoc_generics.own_params.clone();
341
342        let parent_generics = tcx.generics_of(impl_local_def_id.to_def_id());
343        let parent_count = parent_generics.parent_count + parent_generics.own_params.len();
344
345        for param in &mut own_params {
346            param.index = param.index + parent_count as u32 - trait_assoc_parent_count as u32;
347        }
348
349        let param_def_id_to_index =
350            own_params.iter().map(|param| (param.def_id, param.index)).collect();
351
352        ty::Generics {
353            parent: Some(impl_local_def_id.to_def_id()),
354            parent_count,
355            own_params,
356            param_def_id_to_index,
357            has_self: false,
358            has_late_bound_regions: trait_assoc_generics.has_late_bound_regions,
359        }
360    });
361
362    // There are no inferred outlives for the synthesized associated type.
363    impl_assoc_ty.inferred_outlives_of(&[]);
364
365    local_def_id
366}