rustc_middle/ty/
assoc.rs

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