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