rustc_middle/ty/
assoc.rs

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