flux_middle/
lib.rs

1//! This crate contains common type definitions that are used by other crates.
2#![feature(
3    associated_type_defaults,
4    box_patterns,
5    closure_track_caller,
6    if_let_guard,
7    map_try_insert,
8    min_specialization,
9    never_type,
10    rustc_private,
11    unwrap_infallible,
12    new_range_api
13)]
14
15extern crate rustc_abi;
16extern crate rustc_ast;
17extern crate rustc_data_structures;
18extern crate rustc_errors;
19extern crate rustc_hir;
20extern crate rustc_index;
21extern crate rustc_macros;
22extern crate rustc_middle;
23extern crate rustc_serialize;
24extern crate rustc_span;
25extern crate rustc_type_ir;
26
27extern crate self as flux_middle;
28
29pub mod big_int;
30mod builtin_assoc_refts;
31pub mod cstore;
32pub mod def_id;
33pub mod fhir;
34pub mod global_env;
35pub mod metrics;
36pub mod pretty;
37pub mod queries;
38pub mod rty;
39mod sort_of;
40
41use std::sync::LazyLock;
42
43use flux_arc_interner::List;
44use flux_macros::fluent_messages;
45pub use flux_rustc_bridge::def_id_to_string;
46use flux_rustc_bridge::{
47    mir::{LocalDecls, PlaceElem},
48    ty::{self, GenericArgsExt},
49};
50use flux_syntax::surface::{self, NodeId};
51use global_env::GlobalEnv;
52use liquid_fixpoint::ThyFunc;
53use queries::QueryResult;
54use rty::VariantIdx;
55use rustc_abi::FieldIdx;
56use rustc_data_structures::{
57    fx::FxIndexMap,
58    unord::{UnordMap, UnordSet},
59};
60use rustc_hir::OwnerId;
61use rustc_macros::{Decodable, Encodable, extension};
62use rustc_middle::ty::TyCtxt;
63use rustc_span::{
64    Symbol,
65    def_id::{DefId, LocalDefId},
66    symbol::Ident,
67};
68
69fluent_messages! { "../locales/en-US.ftl" }
70
71#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Encodable, Decodable)]
72pub enum PanicSpec {
73    WillNotPanic,
74    MightPanic(PanicReason),
75}
76
77#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Encodable, Decodable)]
78pub enum PanicReason {
79    Transitive,
80    UnresolvedCall(DefId),
81    DynamicDispatch,
82    SynthesizedPanic,
83    NotInCallGraph,
84    NoMIRAvailable,
85}
86
87pub struct TheoryFunc {
88    pub name: Symbol,
89    pub sort: rty::PolyFuncSort,
90    pub itf: liquid_fixpoint::ThyFunc,
91}
92
93pub static THEORY_FUNCS: LazyLock<UnordMap<liquid_fixpoint::ThyFunc, TheoryFunc>> =
94    LazyLock::new(|| {
95        use rty::{BvSize, Sort::*};
96        liquid_fixpoint::ThyFunc::ALL
97            .into_iter()
98            .filter_map(|func| {
99                let func = TheoryFunc {
100                    name: Symbol::intern(name_of_thy_func(func)?),
101                    itf: func,
102                    sort: sort_of_thy_func(func)?,
103                };
104                Some(func)
105            })
106            .chain([
107                // we can't express these as function types so we add special case
108                TheoryFunc {
109                    name: Symbol::intern("bv_zero_extend_32_to_64"),
110                    itf: liquid_fixpoint::ThyFunc::BvZeroExtend(32),
111                    sort: rty::PolyFuncSort::new(
112                        List::empty(),
113                        rty::FuncSort::new(
114                            vec![BitVec(BvSize::Fixed(32))],
115                            BitVec(BvSize::Fixed(64)),
116                        ),
117                    ),
118                },
119                TheoryFunc {
120                    name: Symbol::intern("bv_sign_extend_32_to_64"),
121                    itf: liquid_fixpoint::ThyFunc::BvSignExtend(32),
122                    sort: rty::PolyFuncSort::new(
123                        List::empty(),
124                        rty::FuncSort::new(
125                            vec![BitVec(BvSize::Fixed(32))],
126                            BitVec(BvSize::Fixed(64)),
127                        ),
128                    ),
129                },
130            ])
131            .map(|func| (func.itf, func))
132            .collect()
133    });
134
135pub fn name_of_thy_func(func: liquid_fixpoint::ThyFunc) -> Option<&'static str> {
136    let name = match func {
137        ThyFunc::BvZeroExtend(_) | ThyFunc::BvSignExtend(_) => return None,
138        ThyFunc::StrLen => "str_len",
139        ThyFunc::StrConcat => "str_concat",
140        ThyFunc::StrPrefixOf => "str_prefix_of",
141        ThyFunc::StrSuffixOf => "str_suffix_of",
142        ThyFunc::StrContains => "str_contains",
143        ThyFunc::IntToBv8 => "bv_int_to_bv8",
144        ThyFunc::Bv8ToInt => "bv_bv8_to_int",
145        ThyFunc::IntToBv32 => "bv_int_to_bv32",
146        ThyFunc::Bv32ToInt => "bv_bv32_to_int",
147        ThyFunc::IntToBv64 => "bv_int_to_bv64",
148        ThyFunc::Bv64ToInt => "bv_bv64_to_int",
149        ThyFunc::BvUge => "bv_uge",
150        ThyFunc::BvSge => "bv_sge",
151        ThyFunc::BvUdiv => "bv_udiv",
152        ThyFunc::BvSdiv => "bv_sdiv",
153        ThyFunc::BvSrem => "bv_srem",
154        ThyFunc::BvUrem => "bv_urem",
155        ThyFunc::BvLshr => "bv_lshr",
156        ThyFunc::BvAshr => "bv_ashr",
157        ThyFunc::BvAnd => "bv_and",
158        ThyFunc::BvOr => "bv_or",
159        ThyFunc::BvXor => "bv_xor",
160        ThyFunc::BvNot => "bv_not",
161        ThyFunc::BvAdd => "bv_add",
162        ThyFunc::BvNeg => "bv_neg",
163        ThyFunc::BvSub => "bv_sub",
164        ThyFunc::BvMul => "bv_mul",
165        ThyFunc::BvShl => "bv_shl",
166        ThyFunc::BvUle => "bv_ule",
167        ThyFunc::BvSle => "bv_sle",
168        ThyFunc::BvUgt => "bv_ugt",
169        ThyFunc::BvSgt => "bv_sgt",
170        ThyFunc::BvUlt => "bv_ult",
171        ThyFunc::BvSlt => "bv_slt",
172        ThyFunc::SetEmpty => "set_empty",
173        ThyFunc::SetSng => "set_singleton",
174        ThyFunc::SetCup => "set_union",
175        ThyFunc::SetCap => "set_intersection",
176        ThyFunc::SetDif => "set_difference",
177        ThyFunc::SetSub => "set_subset",
178        ThyFunc::SetMem => "set_is_in",
179        ThyFunc::MapDefault => "map_default",
180        ThyFunc::MapSelect => "map_select",
181        ThyFunc::MapStore => "map_store",
182    };
183    Some(name)
184}
185
186fn sort_of_thy_func(func: liquid_fixpoint::ThyFunc) -> Option<rty::PolyFuncSort> {
187    use rty::{
188        BvSize, ParamSort,
189        Sort::{self, *},
190        SortCtor::*,
191        SortParamKind,
192    };
193    let param0 = ParamSort::from_u32(0);
194    let param1 = ParamSort::from_u32(1);
195    let bv_param0 = BvSize::Param(ParamSort::from_u32(0));
196
197    let sort = match func {
198        ThyFunc::BvZeroExtend(_) | ThyFunc::BvSignExtend(_) => return None,
199        ThyFunc::StrLen => {
200            // str -> int
201            rty::PolyFuncSort::new(List::empty(), rty::FuncSort::new(vec![rty::Sort::Str], Int))
202        }
203        ThyFunc::StrConcat => {
204            // (str, str) -> str
205            rty::PolyFuncSort::new(
206                List::empty(),
207                rty::FuncSort::new(vec![rty::Sort::Str, rty::Sort::Str], rty::Sort::Str),
208            )
209        }
210        ThyFunc::StrPrefixOf | ThyFunc::StrSuffixOf | ThyFunc::StrContains => {
211            // (str, str) -> bool
212            rty::PolyFuncSort::new(
213                List::empty(),
214                rty::FuncSort::new(vec![rty::Sort::Str, rty::Sort::Str], Bool),
215            )
216        }
217        ThyFunc::IntToBv8 => {
218            // int -> BitVec<8>
219            rty::PolyFuncSort::new(
220                List::empty(),
221                rty::FuncSort::new(vec![rty::Sort::Int], BitVec(BvSize::Fixed(8))),
222            )
223        }
224        ThyFunc::Bv8ToInt => {
225            // BitVec<8> -> int
226            rty::PolyFuncSort::new(
227                List::empty(),
228                rty::FuncSort::new(vec![BitVec(BvSize::Fixed(8))], Int),
229            )
230        }
231        ThyFunc::IntToBv32 => {
232            // int -> BitVec<32>
233            rty::PolyFuncSort::new(
234                List::empty(),
235                rty::FuncSort::new(vec![rty::Sort::Int], BitVec(BvSize::Fixed(32))),
236            )
237        }
238        ThyFunc::Bv32ToInt => {
239            // BitVec<32> -> int
240            rty::PolyFuncSort::new(
241                List::empty(),
242                rty::FuncSort::new(vec![BitVec(BvSize::Fixed(32))], Int),
243            )
244        }
245        ThyFunc::IntToBv64 => {
246            // int -> BitVec<64>
247            rty::PolyFuncSort::new(
248                List::empty(),
249                rty::FuncSort::new(vec![rty::Sort::Int], BitVec(BvSize::Fixed(64))),
250            )
251        }
252        ThyFunc::Bv64ToInt => {
253            // BitVec<64> -> int
254            rty::PolyFuncSort::new(
255                List::empty(),
256                rty::FuncSort::new(vec![BitVec(BvSize::Fixed(64))], Int),
257            )
258        }
259        ThyFunc::BvUdiv
260        | ThyFunc::BvSdiv
261        | ThyFunc::BvSrem
262        | ThyFunc::BvUrem
263        | ThyFunc::BvLshr
264        | ThyFunc::BvAshr
265        | ThyFunc::BvAnd
266        | ThyFunc::BvOr
267        | ThyFunc::BvXor
268        | ThyFunc::BvAdd
269        | ThyFunc::BvSub
270        | ThyFunc::BvMul
271        | ThyFunc::BvShl => {
272            // ∀s. (BitVec<s>, BitVec<s>) -> BitVec<s>
273            rty::PolyFuncSort::new(
274                List::singleton(SortParamKind::BvSize),
275                rty::FuncSort::new(vec![BitVec(bv_param0), BitVec(bv_param0)], BitVec(bv_param0)),
276            )
277        }
278        ThyFunc::BvNot | ThyFunc::BvNeg => {
279            // ∀s. BitVec<s> -> BitVec<s>
280            rty::PolyFuncSort::new(
281                List::singleton(SortParamKind::BvSize),
282                rty::FuncSort::new(vec![BitVec(bv_param0)], BitVec(bv_param0)),
283            )
284        }
285        ThyFunc::BvUgt
286        | ThyFunc::BvSgt
287        | ThyFunc::BvUlt
288        | ThyFunc::BvSlt
289        | ThyFunc::BvSle
290        | ThyFunc::BvUge
291        | ThyFunc::BvSge
292        | ThyFunc::BvUle => {
293            // ∀s. (BitVec<s>, BitVec<s>) -> bool
294            rty::PolyFuncSort::new(
295                List::singleton(SortParamKind::BvSize),
296                rty::FuncSort::new(vec![BitVec(bv_param0), BitVec(bv_param0)], Bool),
297            )
298        }
299        ThyFunc::SetEmpty => {
300            // ∀s. int -> Set<S> why does this take an int?
301            rty::PolyFuncSort::new(
302                List::singleton(SortParamKind::Sort),
303                rty::FuncSort::new(vec![Int], Sort::app(Set, List::singleton(Var(param0)))),
304            )
305        }
306        ThyFunc::SetSng => {
307            // ∀s. s -> Set<S>
308            rty::PolyFuncSort::new(
309                List::singleton(SortParamKind::Sort),
310                rty::FuncSort::new(vec![Var(param0)], Sort::app(Set, List::singleton(Var(param0)))),
311            )
312        }
313        ThyFunc::SetCup | ThyFunc::SetCap | ThyFunc::SetDif => {
314            // ∀s. (Set<S>, Set<S>) -> Set<S>
315            rty::PolyFuncSort::new(
316                List::singleton(SortParamKind::Sort),
317                rty::FuncSort::new(
318                    vec![
319                        Sort::app(Set, List::singleton(Var(param0))),
320                        Sort::app(Set, List::singleton(Var(param0))),
321                    ],
322                    Sort::app(Set, List::singleton(Var(param0))),
323                ),
324            )
325        }
326        ThyFunc::SetMem => {
327            // ∀s. (s, Set<S>) -> bool
328            rty::PolyFuncSort::new(
329                List::singleton(SortParamKind::Sort),
330                rty::FuncSort::new(
331                    vec![Var(param0), Sort::app(Set, List::singleton(Var(param0)))],
332                    Bool,
333                ),
334            )
335        }
336        ThyFunc::SetSub => {
337            // ∀s. (Set<s>, Set<s>) -> bool
338            rty::PolyFuncSort::new(
339                List::singleton(SortParamKind::Sort),
340                rty::FuncSort::new(
341                    vec![
342                        Sort::app(Set, List::singleton(Var(param0))),
343                        Sort::app(Set, List::singleton(Var(param0))),
344                    ],
345                    Bool,
346                ),
347            )
348        }
349
350        ThyFunc::MapDefault => {
351            // ∀k,v. v -> Map<k,v>
352            rty::PolyFuncSort::new(
353                List::from_arr([SortParamKind::Sort, SortParamKind::Sort]),
354                rty::FuncSort::new(
355                    vec![Var(param1)],
356                    Sort::app(Map, List::from_arr([Var(param0), Var(param1)])),
357                ),
358            )
359        }
360        ThyFunc::MapSelect => {
361            // ∀k,v. (Map<k,v>, k) -> v
362            rty::PolyFuncSort::new(
363                List::from_arr([SortParamKind::Sort, SortParamKind::Sort]),
364                rty::FuncSort::new(
365                    vec![Sort::app(Map, List::from_arr([Var(param0), Var(param1)])), Var(param0)],
366                    Var(param1),
367                ),
368            )
369        }
370        ThyFunc::MapStore => {
371            // ∀k,v. (Map<k,v>, k, v) -> Map<k, v>
372            rty::PolyFuncSort::new(
373                List::from_arr([SortParamKind::Sort, SortParamKind::Sort]),
374                rty::FuncSort::new(
375                    vec![
376                        Sort::app(Map, List::from_arr([Var(param0), Var(param1)])),
377                        Var(param0),
378                        Var(param1),
379                    ],
380                    Sort::app(Map, List::from_arr([Var(param0), Var(param1)])),
381                ),
382            )
383        }
384    };
385    Some(sort)
386}
387
388#[derive(Default)]
389pub struct Specs {
390    items: UnordMap<OwnerId, surface::Item>,
391    trait_items: UnordMap<OwnerId, surface::TraitItemFn>,
392    impl_items: UnordMap<OwnerId, surface::ImplItemFn>,
393    pub flux_items_by_parent: FxIndexMap<OwnerId, Vec<surface::FluxItem>>,
394    /// Set of dummy items generated by the extern spec macro we must completely ignore. This is
395    /// not the same as [ignored items] because, for ignored items, we still need to return errors
396    /// for queries and handle them gracefully in order to report them at the use it.
397    ///
398    /// If an item is in this set, all its descendants are also consider dummy (but they may not be
399    /// in the set).
400    ///
401    /// [ignored items]: Specs::ignores
402    dummy_extern: UnordSet<LocalDefId>,
403    extern_id_to_local_id: UnordMap<DefId, LocalDefId>,
404    local_id_to_extern_id: UnordMap<LocalDefId, DefId>,
405}
406
407impl Specs {
408    pub fn insert_extern_spec_id_mapping(
409        &mut self,
410        local_id: LocalDefId,
411        extern_id: DefId,
412    ) -> Result<(), ExternSpecMappingErr> {
413        #[expect(
414            clippy::disallowed_methods,
415            reason = "we are inserting the extern spec mapping and we want to ensure it doesn't point to a local item"
416        )]
417        if let Some(local) = extern_id.as_local() {
418            return Err(ExternSpecMappingErr::IsLocal(local));
419        }
420        if let Err(err) = self.extern_id_to_local_id.try_insert(extern_id, local_id) {
421            return Err(ExternSpecMappingErr::Dup(*err.entry.get()));
422        }
423        self.local_id_to_extern_id.insert(local_id, extern_id);
424        Ok(())
425    }
426
427    pub fn insert_dummy(&mut self, def_id: LocalDefId) {
428        self.dummy_extern.insert(def_id);
429    }
430
431    pub fn get_item(&self, owner_id: OwnerId) -> Option<&surface::Item> {
432        self.items.get(&owner_id)
433    }
434
435    pub fn insert_item(&mut self, owner_id: OwnerId, item: surface::Item) -> Option<surface::Item> {
436        if let Some(old) = self.items.insert(owner_id, item) {
437            return Some(old);
438        }
439        None
440    }
441
442    pub fn get_trait_item(&self, owner_id: OwnerId) -> Option<&surface::TraitItemFn> {
443        self.trait_items.get(&owner_id)
444    }
445
446    pub fn insert_trait_item(
447        &mut self,
448        owner_id: OwnerId,
449        trait_item: surface::TraitItemFn,
450    ) -> Option<surface::TraitItemFn> {
451        if let Some(old) = self.trait_items.insert(owner_id, trait_item) {
452            return Some(old);
453        }
454        None
455    }
456
457    pub fn get_impl_item(&self, owner_id: OwnerId) -> Option<&surface::ImplItemFn> {
458        self.impl_items.get(&owner_id)
459    }
460
461    pub fn insert_impl_item(
462        &mut self,
463        owner_id: OwnerId,
464        impl_item: surface::ImplItemFn,
465    ) -> Option<surface::ImplItemFn> {
466        if let Some(old) = self.impl_items.insert(owner_id, impl_item) {
467            return Some(old);
468        }
469        None
470    }
471}
472
473/// Represents errors that can occur when inserting a mapping between a `LocalDefId` and a `DefId`
474/// for an extern spec.
475pub enum ExternSpecMappingErr {
476    /// Indicates that the [`DefId`] we are trying to add extern specs to is actually local. Returns
477    /// the [`DefId`] as a [`LocalDefId`].
478    IsLocal(LocalDefId),
479
480    /// Indicates that there is an existing extern spec for the given extern id. Returns the existing
481    /// `LocalDefId` that maps to the extern id.
482    ///
483    /// NOTE: This currently only considers extern specs defined in the local crate. There could still
484    /// be duplicates if an extern spec is imported from an external crate. In such cases, the local
485    /// extern spec takes precedence. Probably, we should at least warn about this, but it's a bit
486    /// tricky because we need to look at the crate metadata which we don't have handy when
487    /// collecting specs.
488    Dup(LocalDefId),
489}
490
491#[derive(Default)]
492pub struct ResolverOutput {
493    pub path_res_map: UnordMap<NodeId, fhir::PartialRes>,
494    /// Resolution of explicitly and implicitly scoped parameters. The [`fhir::ParamId`] is unique
495    /// per item. The [`NodeId`] used as the key corresponds to the node introducing the parameter.
496    /// When explicit, this is the id of the [`surface::GenericArg`] or [`surface::RefineParam`],
497    /// when implicit, this is the id of the [`surface::RefineArg::Bind`] or [`surface::FnInput`].
498    pub param_res_map: UnordMap<NodeId, (fhir::ParamId, fhir::ParamKind)>,
499    /// List of implicitly scoped params defined in a scope. The [`NodeId`] used as key is the id of
500    /// the node introducing the scope, e.g., [`surface::FnSig`], [`surface::FnOutput`], or
501    /// [`surface::VariantDef`]. The [`NodeId`]s in the vectors are keys in [`Self::param_res_map`].
502    pub implicit_params: UnordMap<NodeId, Vec<(Ident, NodeId)>>,
503    pub sort_path_res_map: UnordMap<NodeId, fhir::SortRes>,
504    pub expr_path_res_map: UnordMap<NodeId, fhir::PartialRes<fhir::ParamId>>,
505    /// The resolved list of local qualifiers per function.
506    /// The [`NodeId`] corresponds to the [`surface::FnSpec`].
507    pub qualifier_res_map: UnordMap<NodeId, Vec<def_id::FluxLocalDefId>>,
508    /// The resolved list of local reveals per function
509    /// The [`NodeId`] corresponds to the [`surface::FnSpec`].
510    pub reveal_res_map: UnordMap<NodeId, Vec<def_id::FluxDefId>>,
511    /// The resolved type param `DefId`s for `#[assume_parametric(...)]` per function.
512    /// The [`NodeId`] corresponds to the surface item's `node_id`.
513    pub parametric_param_res_map: UnordMap<NodeId, Vec<DefId>>,
514}
515
516#[extension(pub trait PlaceExt)]
517impl flux_rustc_bridge::mir::Place {
518    fn ty(&self, genv: GlobalEnv, local_decls: &LocalDecls) -> QueryResult<PlaceTy> {
519        self.projection
520            .iter()
521            .try_fold(PlaceTy::from_ty(local_decls[self.local].ty.clone()), |place_ty, elem| {
522                place_ty.projection_ty(genv, *elem)
523            })
524    }
525
526    fn behind_raw_ptr(&self, genv: GlobalEnv, local_decls: &LocalDecls) -> QueryResult<bool> {
527        let mut place_ty = PlaceTy::from_ty(local_decls[self.local].ty.clone());
528        for elem in &self.projection {
529            if let (PlaceElem::Deref, ty::TyKind::RawPtr(..)) = (elem, place_ty.ty.kind()) {
530                return Ok(true);
531            }
532            place_ty = place_ty.projection_ty(genv, *elem)?;
533        }
534        Ok(false)
535    }
536}
537
538#[derive(Debug)]
539pub struct PlaceTy {
540    pub ty: ty::Ty,
541    /// Downcast to a particular variant of an enum or a generator, if included.
542    pub variant_index: Option<VariantIdx>,
543}
544
545impl PlaceTy {
546    fn from_ty(ty: ty::Ty) -> PlaceTy {
547        PlaceTy { ty, variant_index: None }
548    }
549
550    fn projection_ty(&self, genv: GlobalEnv, elem: PlaceElem) -> QueryResult<PlaceTy> {
551        if self.variant_index.is_some() && !matches!(elem, PlaceElem::Field(..)) {
552            Err(query_bug!("cannot use non field projection on downcasted place"))?;
553        }
554        let place_ty = match elem {
555            PlaceElem::Deref => PlaceTy::from_ty(self.ty.deref()),
556            PlaceElem::Field(fld) => PlaceTy::from_ty(self.field_ty(genv, fld)?),
557            PlaceElem::Downcast(_, variant_idx) => {
558                PlaceTy { ty: self.ty.clone(), variant_index: Some(variant_idx) }
559            }
560            PlaceElem::Index(_) | PlaceElem::ConstantIndex { .. } => {
561                if let ty::TyKind::Array(ty, _) | ty::TyKind::Slice(ty) = self.ty.kind() {
562                    PlaceTy::from_ty(ty.clone())
563                } else {
564                    return Err(query_bug!("cannot use non field projection on downcasted place"));
565                }
566            }
567        };
568        Ok(place_ty)
569    }
570
571    fn field_ty(&self, genv: GlobalEnv, f: FieldIdx) -> QueryResult<ty::Ty> {
572        match self.ty.kind() {
573            ty::TyKind::Adt(adt_def, args) => {
574                let variant_def = match self.variant_index {
575                    None => adt_def.non_enum_variant(),
576                    Some(variant_index) => {
577                        assert!(adt_def.is_enum());
578                        adt_def.variant(variant_index)
579                    }
580                };
581                let field_def = &variant_def.fields[f];
582                let ty = genv.lower_type_of(field_def.did)?;
583                Ok(ty.subst(args))
584            }
585            ty::TyKind::Tuple(tys) => Ok(tys[f.index()].clone()),
586            ty::TyKind::Closure(_, args) => Ok(args.as_closure().upvar_tys()[f.index()].clone()),
587            _ => Err(query_bug!("extracting field of non-tuple non-adt non-closure: {self:?}")),
588        }
589    }
590}
591
592/// The different reasons we issue fixpoint queries. This is used to dissambiguate queries that
593/// are issued for the same item.
594///
595/// NOTE: This is defined here because it's also used in [`metrics`]
596#[derive(Debug, Hash, Clone, Copy)]
597pub enum FixpointQueryKind {
598    /// Query issued when checking an impl method is a subtype of the trait
599    Impl,
600    /// Query issued to check the body of a function
601    Body,
602    /// Query issued to check an (enum) invariant is implied by the type definition
603    Invariant,
604}
605
606impl FixpointQueryKind {
607    pub fn ext(self) -> &'static str {
608        match self {
609            FixpointQueryKind::Impl => "sub.fluxc",
610            FixpointQueryKind::Body => "fluxc",
611            FixpointQueryKind::Invariant => "fluxc",
612        }
613    }
614
615    /// A string that uniquely identifies a query given an item `DefId`
616    pub fn task_key(self, tcx: TyCtxt, def_id: DefId) -> String {
617        format!("{}###{:?}", tcx.def_path_str(def_id), self)
618    }
619
620    /// Returns `true` if the fixpoint query kind is [`Body`].
621    ///
622    /// [`Body`]: FixpointQueryKind::Body
623    #[must_use]
624    pub fn is_body(&self) -> bool {
625        matches!(self, Self::Body)
626    }
627}