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