Skip to main content

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}