flux_middle/def_id.rs
1use flux_common::bug;
2use rustc_hir::def_id::{CrateNum, DefId, DefIndex, LocalDefId};
3use rustc_macros::{Decodable, Encodable};
4use rustc_span::Symbol;
5
6/// An id for a Flux-specific item that doesn't have a corresponding Rust item and thus, we cannot
7/// identify it with a [`DefId`]. This includes, for example, associated refinements, qualifiers
8/// and refinement functions.
9///
10/// # Type-level invariant
11/// This struct maintains a type-level invariant ensuring that the referenced item exists. The id is
12/// composed of two parts:
13/// * `parent`: A reference to a parent Rust item
14/// * `name`: A name uniquely identifiying the item within the parent
15///
16/// Since the name can be an arbitrary [`Symbol`], this doesn't guarantee the existence of the item,
17/// so we must be careful when creating instances of this struct.
18///
19/// # Implementation Details
20/// * Fields are private to ensure construction only through [`FluxId::new`]
21/// * A clippy lint prevents direct usage of [`FluxId::new`], which can be selectively disabled
22/// when item existence is guaranteed
23/// * The type is parametric over the parent `Id` type to support various id types (e.g., [`DefId`],
24/// [`MaybeExternId`])
25#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Encodable, Decodable)]
26pub struct FluxId<Id> {
27 parent: Id,
28 name: Symbol,
29}
30
31pub type FluxDefId = FluxId<DefId>;
32pub type FluxLocalDefId = FluxId<LocalDefId>;
33
34impl<Id> FluxId<Id> {
35 pub fn new(parent: Id, name: Symbol) -> Self {
36 Self { parent, name }
37 }
38
39 pub fn parent(self) -> Id {
40 self.parent
41 }
42
43 pub fn name(self) -> Symbol {
44 self.name
45 }
46}
47
48impl FluxDefId {
49 pub fn as_local(self) -> Option<FluxLocalDefId> {
50 #[allow(
51 clippy::disallowed_methods,
52 reason = "we also have a warning for `FluxId::as_local`"
53 )]
54 Some(FluxId { parent: self.parent.as_local()?, name: self.name })
55 }
56
57 #[track_caller]
58 pub fn expect_local(self) -> FluxLocalDefId {
59 #[allow(
60 clippy::disallowed_methods,
61 reason = "we also have a warning for `FluxId::expect_local`"
62 )]
63 match self.as_local() {
64 Some(local_id) => local_id,
65 None => panic!("FluxDefId::expect_local: `{self:?}` isn't local"),
66 }
67 }
68
69 pub fn krate(self) -> CrateNum {
70 self.parent.krate
71 }
72
73 pub fn index(self) -> FluxId<DefIndex> {
74 FluxId { parent: self.parent.index, name: self.name }
75 }
76}
77
78impl FluxLocalDefId {
79 pub fn to_def_id(self) -> FluxDefId {
80 FluxDefId { parent: self.parent.to_def_id(), name: self.name }
81 }
82
83 pub fn local_def_index(self) -> FluxId<DefIndex> {
84 FluxId { parent: self.parent.local_def_index, name: self.name }
85 }
86}
87
88impl FluxId<MaybeExternId> {
89 pub fn local_id(self) -> FluxLocalDefId {
90 FluxLocalDefId { parent: self.parent.local_id(), name: self.name }
91 }
92}
93
94impl rustc_middle::query::IntoQueryParam<FluxDefId> for FluxLocalDefId {
95 fn into_query_param(self) -> FluxDefId {
96 self.to_def_id()
97 }
98}
99
100/// This enum serves as a type-level reminder that a local definition _may be_ a wrapper for an
101/// extern spec. This abstraction is not infallible, so one should be careful and decide in each
102/// situation whether to use the [_local id_] or the [_resolved id_].
103///
104/// The construction of [`MaybeExternId`] is not encapsulated, but it is recommended to use
105/// [`GlobalEnv::maybe_extern_id`] to create one.
106///
107/// The enum is generic on the local `Id` as we use it with various kinds of local ids, e.g.,
108/// [`LocalDefId`], [`DefId`], ...
109///
110/// [_local id_]: MaybeExternId::local_id
111/// [_resolved id_]: MaybeExternId::resolved_id
112/// [`GlobalEnv::maybe_extern_id`]: crate::global_env::GlobalEnv::maybe_extern_id
113#[derive(Clone, Copy, Debug)]
114pub enum MaybeExternId<Id = LocalDefId> {
115 /// An id for a local spec.
116 Local(Id),
117 /// A "dummy" local definition wrapping an external spec. The `Id` is the local id of the definition
118 /// corresponding to the extern spec. The `DefId` is the _resolved_ id for the external definition.
119 Extern(Id, DefId),
120}
121
122impl<Id> MaybeExternId<Id> {
123 pub fn map<R>(self, f: impl FnOnce(Id) -> R) -> MaybeExternId<R> {
124 match self {
125 MaybeExternId::Local(local_id) => MaybeExternId::Local(f(local_id)),
126 MaybeExternId::Extern(local_id, def_id) => MaybeExternId::Extern(f(local_id), def_id),
127 }
128 }
129
130 pub fn local_id(self) -> Id {
131 match self {
132 MaybeExternId::Local(local_id) | MaybeExternId::Extern(local_id, _) => local_id,
133 }
134 }
135
136 #[track_caller]
137 pub fn expect_local(self) -> Id {
138 match self {
139 MaybeExternId::Local(local_id) => local_id,
140 MaybeExternId::Extern(..) => bug!("expected `MaybeExternId::Local`"),
141 }
142 }
143
144 /// Returns `true` if the maybe extern id is [`Local`].
145 ///
146 /// [`Local`]: MaybeExternId::Local
147 #[must_use]
148 pub fn is_local(self) -> bool {
149 matches!(self, Self::Local(..))
150 }
151
152 /// Returns `true` if the maybe extern id is [`Extern`].
153 ///
154 /// [`Extern`]: MaybeExternId::Extern
155 #[must_use]
156 pub fn is_extern(&self) -> bool {
157 matches!(self, Self::Extern(..))
158 }
159
160 pub fn as_local(self) -> Option<Id> {
161 if let MaybeExternId::Local(local_id) = self { Some(local_id) } else { None }
162 }
163
164 pub fn as_extern(self) -> Option<DefId> {
165 if let MaybeExternId::Extern(_, def_id) = self { Some(def_id) } else { None }
166 }
167}
168
169impl<Id: Into<DefId>> MaybeExternId<Id> {
170 /// Returns the [`DefId`] this id _truly_ corresponds to, i.e, returns the [`DefId`] of the
171 /// extern definition if [`Extern`] or converts the local id into a [`DefId`] if [`Local`].
172 ///
173 /// [`Local`]: MaybeExternId::Local
174 /// [`Extern`]: MaybeExternId::Extern
175 pub fn resolved_id(self) -> DefId {
176 match self {
177 MaybeExternId::Local(local_id) => local_id.into(),
178 MaybeExternId::Extern(_, def_id) => def_id,
179 }
180 }
181}
182
183impl rustc_middle::query::IntoQueryParam<DefId> for MaybeExternId {
184 fn into_query_param(self) -> DefId {
185 self.resolved_id()
186 }
187}
188
189/// Normally, a [`DefId`] is either local or external, and [`DefId::as_local`] can be used to
190/// distinguish between the two. However, extern specs introduce a third case: a local definition
191/// wrapping an extern spec. This enum is a type level reminder used to differentiate between these
192/// three cases.
193///
194/// The construction of [`ResolvedDefId`] is not encapsulated, but it is recommended to use
195/// [`GlobalEnv::resolve_id`] to create one.
196///
197/// This is used when we are given a [`DefId`] and we need to resolve it into one of these three
198/// cases. For handling local items that may correspond to an extern spec, see [`MaybeExternId`].
199///
200/// [`GlobalEnv::resolve_id`]: crate::global_env::GlobalEnv::resolve_id
201#[derive(Clone, Copy)]
202pub enum ResolvedDefId {
203 /// A local definition. Corresponds to [`MaybeExternId::Local`].
204 Local(LocalDefId),
205 /// A "dummy" local definition wrapping an extern spec. The `LocalDefId` is for the local item,
206 /// and the `DefId` is the resolved id for the external spec. Corresponds to
207 /// [`MaybeExternId::Extern`].
208 ExternSpec(LocalDefId, DefId),
209 /// An external definition with no corresponding (local) extern spec.
210 Extern(DefId),
211}
212
213impl ResolvedDefId {
214 pub fn as_maybe_extern(self) -> Option<MaybeExternId> {
215 match self {
216 ResolvedDefId::Local(local_id) => Some(MaybeExternId::Local(local_id)),
217 ResolvedDefId::ExternSpec(local_id, def_id) => {
218 Some(MaybeExternId::Extern(local_id, def_id))
219 }
220 ResolvedDefId::Extern(_) => None,
221 }
222 }
223}