rustc_middle/ty/
assoc.rs

1use rustc_data_structures::sorted_map::SortedIndexMultiMap;
2use rustc_hir as hir;
3use rustc_hir::attrs::AttributeKind;
4use rustc_hir::def::{DefKind, Namespace};
5use rustc_hir::def_id::DefId;
6use rustc_hir::find_attr;
7use rustc_macros::{Decodable, Encodable, HashStable};
8use rustc_span::{ErrorGuaranteed, Ident, Symbol};
9
10use super::{TyCtxt, Visibility};
11use crate::ty;
12
13#[derive(Clone, Copy, PartialEq, Eq, Debug, HashStable, Hash, Encodable, Decodable)]
14pub enum AssocContainer {
15    Trait,
16    InherentImpl,
17    /// The `DefId` points to the trait item being implemented.
18    TraitImpl(Result<DefId, ErrorGuaranteed>),
19}
20
21/// Information about an associated item
22#[derive(Copy, Clone, Debug, PartialEq, HashStable, Eq, Hash, Encodable, Decodable)]
23pub struct AssocItem {
24    pub def_id: DefId,
25    pub kind: AssocKind,
26    pub container: AssocContainer,
27}
28
29impl AssocItem {
30    // Gets the identifier, if it has one.
31    pub fn opt_name(&self) -> Option<Symbol> {
32        match self.kind {
33            ty::AssocKind::Type { data: AssocTypeData::Normal(name) } => Some(name),
34            ty::AssocKind::Type { data: AssocTypeData::Rpitit(_) } => None,
35            ty::AssocKind::Const { name } => Some(name),
36            ty::AssocKind::Fn { name, .. } => Some(name),
37        }
38    }
39
40    // Gets the identifier name. Aborts if it lacks one, i.e. is an RPITIT
41    // associated type.
42    pub fn name(&self) -> Symbol {
43        self.opt_name().expect("name of non-Rpitit assoc item")
44    }
45
46    pub fn ident(&self, tcx: TyCtxt<'_>) -> Ident {
47        Ident::new(self.name(), tcx.def_ident_span(self.def_id).unwrap())
48    }
49
50    /// Gets the defaultness of the associated item.
51    /// To get the default associated type, use the [`type_of`] query on the
52    /// [`DefId`] of the type.
53    ///
54    /// [`type_of`]: crate::ty::TyCtxt::type_of
55    pub fn defaultness(&self, tcx: TyCtxt<'_>) -> hir::Defaultness {
56        match self.container {
57            AssocContainer::InherentImpl => hir::Defaultness::Final,
58            AssocContainer::Trait | AssocContainer::TraitImpl(_) => tcx.defaultness(self.def_id),
59        }
60    }
61
62    pub fn expect_trait_impl(&self) -> Result<DefId, ErrorGuaranteed> {
63        let AssocContainer::TraitImpl(trait_item_id) = self.container else {
64            bug!("expected item to be in a trait impl: {:?}", self.def_id);
65        };
66        trait_item_id
67    }
68
69    /// If this is a trait impl item, returns the `DefId` of the trait item this implements.
70    /// Otherwise, returns `DefId` for self. Returns an Err in case the trait item was not
71    /// resolved successfully.
72    pub fn trait_item_or_self(&self) -> Result<DefId, ErrorGuaranteed> {
73        match self.container {
74            AssocContainer::TraitImpl(id) => id,
75            AssocContainer::Trait | AssocContainer::InherentImpl => Ok(self.def_id),
76        }
77    }
78
79    pub fn trait_item_def_id(&self) -> Option<DefId> {
80        match self.container {
81            AssocContainer::TraitImpl(Ok(id)) => Some(id),
82            _ => None,
83        }
84    }
85
86    #[inline]
87    pub fn visibility(&self, tcx: TyCtxt<'_>) -> Visibility<DefId> {
88        tcx.visibility(self.def_id)
89    }
90
91    #[inline]
92    pub fn container_id(&self, tcx: TyCtxt<'_>) -> DefId {
93        tcx.parent(self.def_id)
94    }
95
96    #[inline]
97    pub fn trait_container(&self, tcx: TyCtxt<'_>) -> Option<DefId> {
98        match self.container {
99            AssocContainer::InherentImpl | AssocContainer::TraitImpl(_) => None,
100            AssocContainer::Trait => Some(tcx.parent(self.def_id)),
101        }
102    }
103
104    #[inline]
105    pub fn impl_container(&self, tcx: TyCtxt<'_>) -> Option<DefId> {
106        match self.container {
107            AssocContainer::InherentImpl | AssocContainer::TraitImpl(_) => {
108                Some(tcx.parent(self.def_id))
109            }
110            AssocContainer::Trait => None,
111        }
112    }
113
114    pub fn signature(&self, tcx: TyCtxt<'_>) -> String {
115        match self.kind {
116            ty::AssocKind::Fn { .. } => {
117                // We skip the binder here because the binder would deanonymize all
118                // late-bound regions, and we don't want method signatures to show up
119                // `as for<'r> fn(&'r MyType)`. Pretty-printing handles late-bound
120                // regions just fine, showing `fn(&MyType)`.
121                tcx.fn_sig(self.def_id).instantiate_identity().skip_binder().to_string()
122            }
123            ty::AssocKind::Type { .. } => format!("type {};", self.name()),
124            ty::AssocKind::Const { name } => {
125                format!("const {}: {:?};", name, tcx.type_of(self.def_id).instantiate_identity())
126            }
127        }
128    }
129
130    pub fn descr(&self) -> &'static str {
131        match self.kind {
132            ty::AssocKind::Const { .. } => "associated const",
133            ty::AssocKind::Fn { has_self: true, .. } => "method",
134            ty::AssocKind::Fn { has_self: false, .. } => "associated function",
135            ty::AssocKind::Type { .. } => "associated type",
136        }
137    }
138
139    pub fn namespace(&self) -> Namespace {
140        match self.kind {
141            ty::AssocKind::Type { .. } => Namespace::TypeNS,
142            ty::AssocKind::Const { .. } | ty::AssocKind::Fn { .. } => Namespace::ValueNS,
143        }
144    }
145
146    pub fn as_def_kind(&self) -> DefKind {
147        match self.kind {
148            AssocKind::Const { .. } => DefKind::AssocConst,
149            AssocKind::Fn { .. } => DefKind::AssocFn,
150            AssocKind::Type { .. } => DefKind::AssocTy,
151        }
152    }
153    pub fn is_type(&self) -> bool {
154        matches!(self.kind, ty::AssocKind::Type { .. })
155    }
156
157    pub fn is_fn(&self) -> bool {
158        matches!(self.kind, ty::AssocKind::Fn { .. })
159    }
160
161    pub fn is_method(&self) -> bool {
162        matches!(self.kind, ty::AssocKind::Fn { has_self: true, .. })
163    }
164
165    pub fn as_tag(&self) -> AssocTag {
166        match self.kind {
167            AssocKind::Const { .. } => AssocTag::Const,
168            AssocKind::Fn { .. } => AssocTag::Fn,
169            AssocKind::Type { .. } => AssocTag::Type,
170        }
171    }
172
173    pub fn is_impl_trait_in_trait(&self) -> bool {
174        matches!(self.kind, AssocKind::Type { data: AssocTypeData::Rpitit(_) })
175    }
176
177    /// Returns true if:
178    /// - This trait associated item has the `#[type_const]` attribute,
179    /// - If it is in a trait impl, the item from the original trait has this attribute, or
180    /// - It is an inherent assoc const.
181    pub fn is_type_const_capable(&self, tcx: TyCtxt<'_>) -> bool {
182        if !matches!(self.kind, ty::AssocKind::Const { .. }) {
183            return false;
184        }
185
186        let def_id = match self.container {
187            AssocContainer::Trait => self.def_id,
188            AssocContainer::TraitImpl(Ok(trait_item_did)) => trait_item_did,
189            AssocContainer::TraitImpl(Err(_)) => return false,
190            AssocContainer::InherentImpl => return true,
191        };
192        find_attr!(tcx.get_all_attrs(def_id), AttributeKind::TypeConst(_))
193    }
194}
195
196#[derive(Copy, Clone, PartialEq, Debug, HashStable, Eq, Hash, Encodable, Decodable)]
197pub enum AssocTypeData {
198    Normal(Symbol),
199    /// The associated type comes from an RPITIT. It has no name, and the
200    /// `ImplTraitInTraitData` provides additional information about its
201    /// source.
202    Rpitit(ty::ImplTraitInTraitData),
203}
204
205#[derive(Copy, Clone, PartialEq, Debug, HashStable, Eq, Hash, Encodable, Decodable)]
206pub enum AssocKind {
207    Const { name: Symbol },
208    Fn { name: Symbol, has_self: bool },
209    Type { data: AssocTypeData },
210}
211
212impl AssocKind {
213    pub fn namespace(&self) -> Namespace {
214        match *self {
215            ty::AssocKind::Type { .. } => Namespace::TypeNS,
216            ty::AssocKind::Const { .. } | ty::AssocKind::Fn { .. } => Namespace::ValueNS,
217        }
218    }
219
220    pub fn as_def_kind(&self) -> DefKind {
221        match self {
222            AssocKind::Const { .. } => DefKind::AssocConst,
223            AssocKind::Fn { .. } => DefKind::AssocFn,
224            AssocKind::Type { .. } => DefKind::AssocTy,
225        }
226    }
227}
228
229impl std::fmt::Display for AssocKind {
230    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
231        match self {
232            AssocKind::Fn { has_self: true, .. } => write!(f, "method"),
233            AssocKind::Fn { has_self: false, .. } => write!(f, "associated function"),
234            AssocKind::Const { .. } => write!(f, "associated const"),
235            AssocKind::Type { .. } => write!(f, "associated type"),
236        }
237    }
238}
239
240// Like `AssocKind`, but just the tag, no fields. Used in various kinds of matching.
241#[derive(Clone, Copy, Debug, PartialEq, Eq)]
242pub enum AssocTag {
243    Const,
244    Fn,
245    Type,
246}
247
248/// A list of `ty::AssocItem`s in definition order that allows for efficient lookup by name.
249///
250/// When doing lookup by name, we try to postpone hygienic comparison for as long as possible since
251/// it is relatively expensive. Instead, items are indexed by `Symbol` and hygienic comparison is
252/// done only on items with the same name.
253#[derive(Debug, Clone, PartialEq, HashStable)]
254pub struct AssocItems {
255    items: SortedIndexMultiMap<u32, Option<Symbol>, ty::AssocItem>,
256}
257
258impl AssocItems {
259    /// Constructs an `AssociatedItems` map from a series of `ty::AssocItem`s in definition order.
260    pub fn new(items_in_def_order: impl IntoIterator<Item = ty::AssocItem>) -> Self {
261        let items = items_in_def_order.into_iter().map(|item| (item.opt_name(), item)).collect();
262        AssocItems { items }
263    }
264
265    /// Returns an iterator over associated items in the order they were defined.
266    ///
267    /// New code should avoid relying on definition order. If you need a particular associated item
268    /// for a known trait, make that trait a lang item instead of indexing this array.
269    pub fn in_definition_order(&self) -> impl '_ + Iterator<Item = &ty::AssocItem> {
270        self.items.iter().map(|(_, v)| v)
271    }
272
273    pub fn len(&self) -> usize {
274        self.items.len()
275    }
276
277    /// Returns an iterator over all associated items with the given name, ignoring hygiene.
278    ///
279    /// Panics if `name.is_empty()` returns `true`.
280    pub fn filter_by_name_unhygienic(
281        &self,
282        name: Symbol,
283    ) -> impl '_ + Iterator<Item = &ty::AssocItem> {
284        assert!(!name.is_empty());
285        self.items.get_by_key(Some(name))
286    }
287
288    /// Returns the associated item with the given identifier and `AssocKind`, if one exists.
289    /// The identifier is ignoring hygiene. This is meant to be used for lints and diagnostics.
290    pub fn filter_by_name_unhygienic_and_kind(
291        &self,
292        name: Symbol,
293        assoc_tag: AssocTag,
294    ) -> impl '_ + Iterator<Item = &ty::AssocItem> {
295        self.filter_by_name_unhygienic(name).filter(move |item| item.as_tag() == assoc_tag)
296    }
297
298    /// Returns the associated item with the given identifier and `AssocKind`, if one exists.
299    /// The identifier is matched hygienically.
300    pub fn find_by_ident_and_kind(
301        &self,
302        tcx: TyCtxt<'_>,
303        ident: Ident,
304        assoc_tag: AssocTag,
305        parent_def_id: DefId,
306    ) -> Option<&ty::AssocItem> {
307        self.filter_by_name_unhygienic(ident.name)
308            .filter(|item| item.as_tag() == assoc_tag)
309            .find(|item| tcx.hygienic_eq(ident, item.ident(tcx), parent_def_id))
310    }
311
312    /// Returns the associated item with the given identifier in the given `Namespace`, if one
313    /// exists. The identifier is matched hygienically.
314    pub fn find_by_ident_and_namespace(
315        &self,
316        tcx: TyCtxt<'_>,
317        ident: Ident,
318        ns: Namespace,
319        parent_def_id: DefId,
320    ) -> Option<&ty::AssocItem> {
321        self.filter_by_name_unhygienic(ident.name)
322            .filter(|item| item.namespace() == ns)
323            .find(|item| tcx.hygienic_eq(ident, item.ident(tcx), parent_def_id))
324    }
325}
326
327impl<'tcx> TyCtxt<'tcx> {
328    /// Given an `fn_def_id` of a trait or a trait implementation:
329    ///
330    /// if `fn_def_id` is a function defined inside a trait, then it synthesizes
331    /// a new def id corresponding to a new associated type for each return-
332    /// position `impl Trait` in the signature.
333    ///
334    /// if `fn_def_id` is a function inside of an impl, then for each synthetic
335    /// associated type generated for the corresponding trait function described
336    /// above, synthesize a corresponding associated type in the impl.
337    pub fn associated_types_for_impl_traits_in_associated_fn(
338        self,
339        fn_def_id: DefId,
340    ) -> &'tcx [DefId] {
341        let parent_def_id = self.parent(fn_def_id);
342        &self.associated_types_for_impl_traits_in_trait_or_impl(parent_def_id)[&fn_def_id]
343    }
344}