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}