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