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