Skip to main content

flux_middle/
lib.rs

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