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 name: Symbol,
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    /// Whether this is a method with an explicit self
30    /// as its first parameter, allowing method calls.
31    pub fn_has_self_parameter: bool,
32
33    /// `Some` if the associated item (an associated type) comes from the
34    /// return-position `impl Trait` in trait desugaring. The `ImplTraitInTraitData`
35    /// provides additional information about its source.
36    pub opt_rpitit_info: Option<ty::ImplTraitInTraitData>,
37}
38
39impl AssocItem {
40    pub fn ident(&self, tcx: TyCtxt<'_>) -> Ident {
41        Ident::new(self.name, tcx.def_ident_span(self.def_id).unwrap())
42    }
43
44    /// Gets the defaultness of the associated item.
45    /// To get the default associated type, use the [`type_of`] query on the
46    /// [`DefId`] of the type.
47    ///
48    /// [`type_of`]: crate::ty::TyCtxt::type_of
49    pub fn defaultness(&self, tcx: TyCtxt<'_>) -> hir::Defaultness {
50        tcx.defaultness(self.def_id)
51    }
52
53    #[inline]
54    pub fn visibility(&self, tcx: TyCtxt<'_>) -> Visibility<DefId> {
55        tcx.visibility(self.def_id)
56    }
57
58    #[inline]
59    pub fn container_id(&self, tcx: TyCtxt<'_>) -> DefId {
60        tcx.parent(self.def_id)
61    }
62
63    #[inline]
64    pub fn trait_container(&self, tcx: TyCtxt<'_>) -> Option<DefId> {
65        match self.container {
66            AssocItemContainer::Impl => None,
67            AssocItemContainer::Trait => Some(tcx.parent(self.def_id)),
68        }
69    }
70
71    #[inline]
72    pub fn impl_container(&self, tcx: TyCtxt<'_>) -> Option<DefId> {
73        match self.container {
74            AssocItemContainer::Impl => Some(tcx.parent(self.def_id)),
75            AssocItemContainer::Trait => None,
76        }
77    }
78
79    pub fn signature(&self, tcx: TyCtxt<'_>) -> String {
80        match self.kind {
81            ty::AssocKind::Fn => {
82                // We skip the binder here because the binder would deanonymize all
83                // late-bound regions, and we don't want method signatures to show up
84                // `as for<'r> fn(&'r MyType)`. Pretty-printing handles late-bound
85                // regions just fine, showing `fn(&MyType)`.
86                tcx.fn_sig(self.def_id).instantiate_identity().skip_binder().to_string()
87            }
88            ty::AssocKind::Type => format!("type {};", self.name),
89            ty::AssocKind::Const => {
90                format!(
91                    "const {}: {:?};",
92                    self.name,
93                    tcx.type_of(self.def_id).instantiate_identity()
94                )
95            }
96        }
97    }
98
99    pub fn descr(&self) -> &'static str {
100        match self.kind {
101            ty::AssocKind::Const => "const",
102            ty::AssocKind::Fn if self.fn_has_self_parameter => "method",
103            ty::AssocKind::Fn => "associated function",
104            ty::AssocKind::Type => "type",
105        }
106    }
107
108    pub fn is_impl_trait_in_trait(&self) -> bool {
109        self.opt_rpitit_info.is_some()
110    }
111
112    /// Returns true if:
113    /// - This trait associated item has the `#[type_const]` attribute,
114    /// - If it is in a trait impl, the item from the original trait has this attribute, or
115    /// - It is an inherent assoc const.
116    pub fn is_type_const_capable(&self, tcx: TyCtxt<'_>) -> bool {
117        if self.kind != ty::AssocKind::Const {
118            return false;
119        }
120
121        let def_id = match (self.container, self.trait_item_def_id) {
122            (AssocItemContainer::Trait, _) => self.def_id,
123            (AssocItemContainer::Impl, Some(trait_item_did)) => trait_item_did,
124            // Inherent impl but this attr is only applied to trait assoc items.
125            (AssocItemContainer::Impl, None) => return true,
126        };
127        tcx.has_attr(def_id, sym::type_const)
128    }
129}
130
131#[derive(Copy, Clone, PartialEq, Debug, HashStable, Eq, Hash, Encodable, Decodable)]
132pub enum AssocKind {
133    Const,
134    Fn,
135    Type,
136}
137
138impl AssocKind {
139    pub fn namespace(&self) -> Namespace {
140        match *self {
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 {
148            AssocKind::Const => DefKind::AssocConst,
149            AssocKind::Fn => DefKind::AssocFn,
150            AssocKind::Type => DefKind::AssocTy,
151        }
152    }
153}
154
155impl std::fmt::Display for AssocKind {
156    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
157        match self {
158            AssocKind::Fn => write!(f, "method"),
159            AssocKind::Const => write!(f, "associated const"),
160            AssocKind::Type => write!(f, "associated type"),
161        }
162    }
163}
164
165/// A list of `ty::AssocItem`s in definition order that allows for efficient lookup by name.
166///
167/// When doing lookup by name, we try to postpone hygienic comparison for as long as possible since
168/// it is relatively expensive. Instead, items are indexed by `Symbol` and hygienic comparison is
169/// done only on items with the same name.
170#[derive(Debug, Clone, PartialEq, HashStable)]
171pub struct AssocItems {
172    items: SortedIndexMultiMap<u32, Symbol, ty::AssocItem>,
173}
174
175impl AssocItems {
176    /// Constructs an `AssociatedItems` map from a series of `ty::AssocItem`s in definition order.
177    pub fn new(items_in_def_order: impl IntoIterator<Item = ty::AssocItem>) -> Self {
178        let items = items_in_def_order.into_iter().map(|item| (item.name, item)).collect();
179        AssocItems { items }
180    }
181
182    /// Returns a slice of associated items in the order they were defined.
183    ///
184    /// New code should avoid relying on definition order. If you need a particular associated item
185    /// for a known trait, make that trait a lang item instead of indexing this array.
186    pub fn in_definition_order(&self) -> impl '_ + Iterator<Item = &ty::AssocItem> {
187        self.items.iter().map(|(_, v)| v)
188    }
189
190    pub fn len(&self) -> usize {
191        self.items.len()
192    }
193
194    /// Returns an iterator over all associated items with the given name, ignoring hygiene.
195    pub fn filter_by_name_unhygienic(
196        &self,
197        name: Symbol,
198    ) -> impl '_ + Iterator<Item = &ty::AssocItem> {
199        self.items.get_by_key(name)
200    }
201
202    /// Returns the associated item with the given name and `AssocKind`, if one exists.
203    pub fn find_by_name_and_kind(
204        &self,
205        tcx: TyCtxt<'_>,
206        ident: Ident,
207        kind: AssocKind,
208        parent_def_id: DefId,
209    ) -> Option<&ty::AssocItem> {
210        self.filter_by_name_unhygienic(ident.name)
211            .filter(|item| item.kind == kind)
212            .find(|item| tcx.hygienic_eq(ident, item.ident(tcx), parent_def_id))
213    }
214
215    /// Returns the associated item with the given name and any of `AssocKind`, if one exists.
216    pub fn find_by_name_and_kinds(
217        &self,
218        tcx: TyCtxt<'_>,
219        ident: Ident,
220        // Sorted in order of what kinds to look at
221        kinds: &[AssocKind],
222        parent_def_id: DefId,
223    ) -> Option<&ty::AssocItem> {
224        kinds.iter().find_map(|kind| self.find_by_name_and_kind(tcx, ident, *kind, parent_def_id))
225    }
226
227    /// Returns the associated item with the given name in the given `Namespace`, if one exists.
228    pub fn find_by_name_and_namespace(
229        &self,
230        tcx: TyCtxt<'_>,
231        ident: Ident,
232        ns: Namespace,
233        parent_def_id: DefId,
234    ) -> Option<&ty::AssocItem> {
235        self.filter_by_name_unhygienic(ident.name)
236            .filter(|item| item.kind.namespace() == ns)
237            .find(|item| tcx.hygienic_eq(ident, item.ident(tcx), parent_def_id))
238    }
239}