flux_middle/
queries.rs

1use std::{
2    cell::{OnceCell, RefCell},
3    rc::Rc,
4};
5
6use flux_arc_interner::List;
7use flux_common::{bug, tracked_span_bug};
8use flux_config as config;
9use flux_errors::{E0999, ErrorGuaranteed};
10use flux_rustc_bridge::{
11    self, def_id_to_string,
12    lowering::{self, Lower, UnsupportedErr},
13    mir::{self},
14    ty,
15};
16use itertools::Itertools;
17use rustc_data_structures::unord::{ExtendUnord, UnordMap};
18use rustc_errors::Diagnostic;
19use rustc_hir::{
20    def::DefKind,
21    def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId},
22};
23use rustc_index::IndexVec;
24use rustc_macros::{Decodable, Encodable};
25use rustc_span::{Span, Symbol};
26
27use crate::{
28    def_id::{FluxDefId, FluxId, MaybeExternId, ResolvedDefId},
29    fhir,
30    global_env::GlobalEnv,
31    rty::{
32        self,
33        refining::{self, Refine, Refiner},
34    },
35};
36
37type Cache<K, V> = RefCell<UnordMap<K, V>>;
38
39pub type QueryResult<T = ()> = Result<T, QueryErr>;
40
41/// An error produced by a query.
42///
43/// We make a distinction between errors reported at def-site and errors reported at use-site.
44///
45/// For most errors reported at the def-site of an item, it makes little sense to check the definition
46/// of dependent items. For example, if a function signature is ill-formed, checking the body of another
47/// function that calls it, can produce confusing errors. In some cases, we can even fail to produce
48/// a signature for a function in which case we can't even check its call sites. For these cases, we
49/// emit an error at the definition site and return a [`QueryErr::Emitted`]. When checking a dependent,
50/// we detect this and early return without reporting any errors at the use-site.
51///
52/// Other errors are better reported at the use-site. For example, if some code calls a function from
53/// an external crate that has unsupported features, we ought to report the error at the call-site,
54/// because it would be confusing to only mention the definition of the external function without
55/// showing which part of the code is calling it. To attach a span to an error one can use [`QueryErr::at`]
56/// to get a [`QueryErrAt`].
57///
58/// Both [`QueryErr`] and [`QueryErrAt`] implement [`Diagnostic`]. The implementation for [`QueryErr`]
59/// reports the error at the definition site, while the implementation for [`QueryErrAt`] reports it at
60/// the (attached) use-site span. This allows us to play a bit lose because we can emit an error without
61/// attaching a span, but this means we may forget to attach spans at some places. We should consider
62/// not implementing [`Diagnostic`] for [`QueryErr`] such that we always make the distinction between
63/// use-site and def-site explicit, e.g., we could have methods `QueryErr::at_use_site` and
64/// `QueryErr::at_def_site` returning types with different implementations of [`Diagnostic`].
65#[derive(Debug, Clone, Encodable, Decodable)]
66pub enum QueryErr {
67    Unsupported {
68        def_id: DefId,
69        err: UnsupportedErr,
70    },
71    Ignored {
72        def_id: DefId,
73    },
74    InvalidGenericArg {
75        def_id: DefId,
76    },
77    MissingAssocReft {
78        impl_id: DefId,
79        trait_id: DefId,
80        name: Symbol,
81    },
82
83    /// An operation tried to access the internals of an opaque struct.
84    OpaqueStruct {
85        struct_id: DefId,
86    },
87    /// Used to report bugs, typically this means executing an arm in a match we thought it was
88    /// unreachable. Use this instead of panicking if it is easy to return a [`QueryErr`]. Use
89    /// [`QueryErr::bug`] or [`crate::query_bug!`] to construct this variant to track source location.
90    Bug {
91        def_id: Option<DefId>,
92        location: String,
93        msg: String,
94    },
95    Emitted(ErrorGuaranteed),
96}
97
98#[macro_export]
99macro_rules! query_bug {
100    ($fmt:literal $(,$args:expr)* $(,)?) => {
101        $crate::queries::QueryErr::bug(None, format_args!($fmt, $($args),*))
102    };
103    ($def_id:expr, $fmt:literal $(,$args:expr)* $(,)? ) => {{
104        $crate::queries::QueryErr::bug(Some($def_id.into()), format_args!($fmt, $($args),*))
105    }};
106}
107
108impl QueryErr {
109    pub fn unsupported(def_id: DefId, err: UnsupportedErr) -> Self {
110        QueryErr::Unsupported { def_id, err }
111    }
112
113    #[track_caller]
114    pub fn bug(def_id: Option<DefId>, msg: impl ToString) -> Self {
115        QueryErr::Bug {
116            def_id,
117            location: format!("{}", std::panic::Location::caller()),
118            msg: msg.to_string(),
119        }
120    }
121
122    pub fn at(self, cx: impl Into<ErrCtxt>) -> QueryErrAt {
123        QueryErrAt { cx: cx.into(), err: self }
124    }
125}
126
127/// A [`QueryErr`] with extra context information
128pub struct QueryErrAt {
129    cx: ErrCtxt,
130    err: QueryErr,
131}
132
133/// The "use site" context in which an error is reported
134#[derive(Clone, Copy)]
135pub enum ErrCtxt {
136    /// The error was triggered when checking a function body. The `Span` is the span in
137    /// the mir associated with the error. The `LocalDefId` is the id of the function.
138    FnCheck(Span, LocalDefId),
139    /// A miscellaneous context for which we only have a span
140    Misc(Span),
141}
142
143impl From<Span> for ErrCtxt {
144    fn from(v: Span) -> Self {
145        Self::Misc(v)
146    }
147}
148
149impl ErrCtxt {
150    fn span(self) -> Span {
151        match self {
152            ErrCtxt::Misc(span) => span,
153            ErrCtxt::FnCheck(span, _) => span,
154        }
155    }
156}
157
158pub struct Providers {
159    pub collect_specs: fn(GlobalEnv) -> crate::Specs,
160    pub resolve_crate: fn(GlobalEnv) -> crate::ResolverOutput,
161    pub desugar: for<'genv> fn(
162        GlobalEnv<'genv, '_>,
163        LocalDefId,
164    ) -> QueryResult<UnordMap<LocalDefId, fhir::Node<'genv>>>,
165    pub fhir_attr_map: for<'genv> fn(GlobalEnv<'genv, '_>, LocalDefId) -> fhir::AttrMap<'genv>,
166    pub fhir_crate: for<'genv> fn(GlobalEnv<'genv, '_>) -> fhir::FluxItems<'genv>,
167    pub qualifiers: fn(GlobalEnv) -> QueryResult<Vec<rty::Qualifier>>,
168    pub prim_rel: fn(GlobalEnv) -> QueryResult<UnordMap<rty::BinOp, rty::PrimRel>>,
169    pub normalized_defns: fn(GlobalEnv) -> rty::NormalizedDefns,
170    pub func_sort: fn(GlobalEnv, FluxId<MaybeExternId>) -> rty::PolyFuncSort,
171    pub func_span: fn(GlobalEnv, FluxId<MaybeExternId>) -> Span,
172    pub adt_sort_def_of: fn(GlobalEnv, MaybeExternId) -> QueryResult<rty::AdtSortDef>,
173    pub check_wf: fn(GlobalEnv, LocalDefId) -> QueryResult<Rc<rty::WfckResults>>,
174    pub adt_def: fn(GlobalEnv, MaybeExternId) -> QueryResult<rty::AdtDef>,
175    pub constant_info: fn(GlobalEnv, MaybeExternId) -> QueryResult<rty::ConstantInfo>,
176    pub type_of: fn(GlobalEnv, MaybeExternId) -> QueryResult<rty::EarlyBinder<rty::TyOrCtor>>,
177    pub variants_of: fn(
178        GlobalEnv,
179        MaybeExternId,
180    ) -> QueryResult<rty::Opaqueness<rty::EarlyBinder<rty::PolyVariants>>>,
181    pub fn_sig: fn(GlobalEnv, MaybeExternId) -> QueryResult<rty::EarlyBinder<rty::PolyFnSig>>,
182    pub generics_of: fn(GlobalEnv, MaybeExternId) -> QueryResult<rty::Generics>,
183    pub refinement_generics_of:
184        fn(GlobalEnv, MaybeExternId) -> QueryResult<rty::EarlyBinder<rty::RefinementGenerics>>,
185    pub predicates_of:
186        fn(GlobalEnv, MaybeExternId) -> QueryResult<rty::EarlyBinder<rty::GenericPredicates>>,
187    pub assoc_refinements_of: fn(GlobalEnv, MaybeExternId) -> QueryResult<rty::AssocRefinements>,
188    pub sort_of_assoc_reft:
189        fn(GlobalEnv, FluxId<MaybeExternId>) -> QueryResult<rty::EarlyBinder<rty::FuncSort>>,
190    pub assoc_refinement_body:
191        fn(GlobalEnv, FluxId<MaybeExternId>) -> QueryResult<rty::EarlyBinder<rty::Lambda>>,
192    #[allow(clippy::type_complexity)]
193    pub default_assoc_refinement_body:
194        fn(GlobalEnv, FluxId<MaybeExternId>) -> QueryResult<Option<rty::EarlyBinder<rty::Lambda>>>,
195    pub item_bounds:
196        fn(GlobalEnv, MaybeExternId) -> QueryResult<rty::EarlyBinder<List<rty::Clause>>>,
197    pub sort_decl_param_count: fn(GlobalEnv, FluxId<MaybeExternId>) -> usize,
198}
199
200macro_rules! empty_query {
201    () => {
202        flux_common::bug!("query not provided")
203    };
204}
205
206impl Default for Providers {
207    fn default() -> Self {
208        Self {
209            collect_specs: |_| empty_query!(),
210            resolve_crate: |_| empty_query!(),
211            desugar: |_, _| empty_query!(),
212            fhir_attr_map: |_, _| empty_query!(),
213            fhir_crate: |_| empty_query!(),
214            normalized_defns: |_| empty_query!(),
215            func_sort: |_, _| empty_query!(),
216            func_span: |_, _| empty_query!(),
217            qualifiers: |_| empty_query!(),
218            prim_rel: |_| empty_query!(),
219            adt_sort_def_of: |_, _| empty_query!(),
220            check_wf: |_, _| empty_query!(),
221            adt_def: |_, _| empty_query!(),
222            type_of: |_, _| empty_query!(),
223            variants_of: |_, _| empty_query!(),
224            fn_sig: |_, _| empty_query!(),
225            generics_of: |_, _| empty_query!(),
226            refinement_generics_of: |_, _| empty_query!(),
227            predicates_of: |_, _| empty_query!(),
228            assoc_refinements_of: |_, _| empty_query!(),
229            assoc_refinement_body: |_, _| empty_query!(),
230            default_assoc_refinement_body: |_, _| empty_query!(),
231            sort_of_assoc_reft: |_, _| empty_query!(),
232            item_bounds: |_, _| empty_query!(),
233            constant_info: |_, _| empty_query!(),
234            sort_decl_param_count: |_, _| empty_query!(),
235        }
236    }
237}
238
239pub struct Queries<'genv, 'tcx> {
240    pub(crate) providers: Providers,
241    mir: Cache<LocalDefId, QueryResult<Rc<mir::BodyRoot<'tcx>>>>,
242    collect_specs: OnceCell<crate::Specs>,
243    resolve_crate: OnceCell<crate::ResolverOutput>,
244    desugar: Cache<LocalDefId, QueryResult<fhir::Node<'genv>>>,
245    fhir_attr_map: Cache<LocalDefId, fhir::AttrMap<'genv>>,
246    fhir_crate: OnceCell<fhir::FluxItems<'genv>>,
247    lower_generics_of: Cache<DefId, ty::Generics<'tcx>>,
248    lower_predicates_of: Cache<DefId, QueryResult<ty::GenericPredicates>>,
249    lower_type_of: Cache<DefId, QueryResult<ty::EarlyBinder<ty::Ty>>>,
250    lower_fn_sig: Cache<DefId, QueryResult<ty::EarlyBinder<ty::PolyFnSig>>>,
251    normalized_defns: Cache<CrateNum, Rc<rty::NormalizedDefns>>,
252    func_sort: Cache<FluxDefId, rty::PolyFuncSort>,
253    func_span: Cache<FluxDefId, Span>,
254    qualifiers: OnceCell<QueryResult<Vec<rty::Qualifier>>>,
255    prim_rel: OnceCell<QueryResult<UnordMap<rty::BinOp, rty::PrimRel>>>,
256    adt_sort_def_of: Cache<DefId, QueryResult<rty::AdtSortDef>>,
257    check_wf: Cache<LocalDefId, QueryResult<Rc<rty::WfckResults>>>,
258    adt_def: Cache<DefId, QueryResult<rty::AdtDef>>,
259    constant_info: Cache<DefId, QueryResult<rty::ConstantInfo>>,
260    generics_of: Cache<DefId, QueryResult<rty::Generics>>,
261    refinement_generics_of: Cache<DefId, QueryResult<rty::EarlyBinder<rty::RefinementGenerics>>>,
262    predicates_of: Cache<DefId, QueryResult<rty::EarlyBinder<rty::GenericPredicates>>>,
263    assoc_refinements_of: Cache<DefId, QueryResult<rty::AssocRefinements>>,
264    assoc_refinement_body: Cache<FluxDefId, QueryResult<rty::EarlyBinder<rty::Lambda>>>,
265    default_assoc_refinement_body:
266        Cache<FluxDefId, QueryResult<Option<rty::EarlyBinder<rty::Lambda>>>>,
267    sort_of_assoc_reft: Cache<FluxDefId, QueryResult<rty::EarlyBinder<rty::FuncSort>>>,
268    item_bounds: Cache<DefId, QueryResult<rty::EarlyBinder<List<rty::Clause>>>>,
269    type_of: Cache<DefId, QueryResult<rty::EarlyBinder<rty::TyOrCtor>>>,
270    variants_of: Cache<DefId, QueryResult<rty::Opaqueness<rty::EarlyBinder<rty::PolyVariants>>>>,
271    fn_sig: Cache<DefId, QueryResult<rty::EarlyBinder<rty::PolyFnSig>>>,
272    lower_late_bound_vars: Cache<LocalDefId, QueryResult<List<ty::BoundVariableKind>>>,
273    sort_decl_param_count: Cache<FluxDefId, usize>,
274    no_panic: Cache<DefId, bool>,
275}
276
277impl<'genv, 'tcx> Queries<'genv, 'tcx> {
278    pub(crate) fn new(providers: Providers) -> Self {
279        Self {
280            providers,
281            mir: Default::default(),
282            collect_specs: Default::default(),
283            resolve_crate: Default::default(),
284            desugar: Default::default(),
285            fhir_attr_map: Default::default(),
286            fhir_crate: Default::default(),
287            lower_generics_of: Default::default(),
288            lower_predicates_of: Default::default(),
289            lower_type_of: Default::default(),
290            lower_fn_sig: Default::default(),
291            normalized_defns: Default::default(),
292            func_sort: Default::default(),
293            func_span: Default::default(),
294            qualifiers: Default::default(),
295            prim_rel: Default::default(),
296            adt_sort_def_of: Default::default(),
297            check_wf: Default::default(),
298            adt_def: Default::default(),
299            constant_info: Default::default(),
300            generics_of: Default::default(),
301            refinement_generics_of: Default::default(),
302            predicates_of: Default::default(),
303            assoc_refinements_of: Default::default(),
304            assoc_refinement_body: Default::default(),
305            default_assoc_refinement_body: Default::default(),
306            sort_of_assoc_reft: Default::default(),
307            item_bounds: Default::default(),
308            type_of: Default::default(),
309            variants_of: Default::default(),
310            fn_sig: Default::default(),
311            lower_late_bound_vars: Default::default(),
312            sort_decl_param_count: Default::default(),
313            no_panic: Default::default(),
314        }
315    }
316
317    pub(crate) fn mir(
318        &self,
319        genv: GlobalEnv<'genv, 'tcx>,
320        def_id: LocalDefId,
321    ) -> QueryResult<Rc<mir::BodyRoot<'tcx>>> {
322        run_with_cache(&self.mir, def_id, || {
323            let mir = unsafe { flux_common::mir_storage::retrieve_mir_body(genv.tcx(), def_id) };
324            let mir =
325                lowering::MirLoweringCtxt::lower_mir_body(genv.tcx(), genv.sess(), def_id, mir)?;
326            Ok(Rc::new(mir))
327        })
328    }
329
330    pub(crate) fn collect_specs(&'genv self, genv: GlobalEnv<'genv, 'tcx>) -> &'genv crate::Specs {
331        self.collect_specs
332            .get_or_init(|| (self.providers.collect_specs)(genv))
333    }
334
335    pub(crate) fn resolve_crate(
336        &'genv self,
337        genv: GlobalEnv<'genv, 'tcx>,
338    ) -> &'genv crate::ResolverOutput {
339        self.resolve_crate
340            .get_or_init(|| (self.providers.resolve_crate)(genv))
341    }
342
343    pub(crate) fn desugar(
344        &'genv self,
345        genv: GlobalEnv<'genv, 'tcx>,
346        def_id: LocalDefId,
347    ) -> QueryResult<fhir::Node<'genv>> {
348        if let Some(v) = self.desugar.borrow().get(&def_id) {
349            return v.clone();
350        }
351        match (self.providers.desugar)(genv, def_id) {
352            Ok(nodes) => {
353                let mut cache = self.desugar.borrow_mut();
354                cache.extend_unord(nodes.into_items().map(|(def_id, node)| (def_id, Ok(node))));
355                let Some(res) = cache.get(&def_id) else {
356                    tracked_span_bug!("cannot desugar {def_id:?}")
357                };
358                res.clone()
359            }
360            Err(err) => {
361                self.desugar.borrow_mut().insert(def_id, Err(err.clone()));
362                Err(err)
363            }
364        }
365    }
366
367    pub(crate) fn fhir_attr_map(
368        &'genv self,
369        genv: GlobalEnv<'genv, 'tcx>,
370        def_id: LocalDefId,
371    ) -> fhir::AttrMap<'genv> {
372        run_with_cache(&self.fhir_attr_map, def_id, || (self.providers.fhir_attr_map)(genv, def_id))
373    }
374
375    pub(crate) fn fhir_crate(
376        &'genv self,
377        genv: GlobalEnv<'genv, 'tcx>,
378    ) -> &'genv fhir::FluxItems<'genv> {
379        self.fhir_crate
380            .get_or_init(|| (self.providers.fhir_crate)(genv))
381    }
382
383    pub(crate) fn lower_generics_of(
384        &self,
385        genv: GlobalEnv<'genv, 'tcx>,
386        def_id: DefId,
387    ) -> ty::Generics<'tcx> {
388        run_with_cache(&self.lower_generics_of, def_id, || {
389            genv.tcx().generics_of(def_id).lower(genv.tcx())
390        })
391    }
392
393    pub(crate) fn lower_predicates_of(
394        &self,
395        genv: GlobalEnv,
396        def_id: DefId,
397    ) -> QueryResult<ty::GenericPredicates> {
398        run_with_cache(&self.lower_predicates_of, def_id, || {
399            genv.tcx()
400                .predicates_of(def_id)
401                .lower(genv.tcx())
402                .map_err(|err| QueryErr::unsupported(def_id, err))
403        })
404    }
405
406    pub(crate) fn lower_type_of(
407        &self,
408        genv: GlobalEnv,
409        def_id: DefId,
410    ) -> QueryResult<ty::EarlyBinder<ty::Ty>> {
411        run_with_cache(&self.lower_type_of, def_id, || {
412            let ty = genv.tcx().type_of(def_id).instantiate_identity();
413            Ok(ty::EarlyBinder(
414                ty.lower(genv.tcx())
415                    .map_err(|err| QueryErr::unsupported(def_id, err.into_err()))?,
416            ))
417        })
418    }
419
420    pub(crate) fn lower_fn_sig(
421        &self,
422        genv: GlobalEnv,
423        def_id: DefId,
424    ) -> QueryResult<ty::EarlyBinder<ty::PolyFnSig>> {
425        run_with_cache(&self.lower_fn_sig, def_id, || {
426            let fn_sig = genv.tcx().fn_sig(def_id).instantiate_identity();
427            Ok(ty::EarlyBinder(
428                fn_sig
429                    .lower(genv.tcx())
430                    .map_err(|err| QueryErr::unsupported(def_id, err.into_err()))?,
431            ))
432        })
433    }
434
435    pub(crate) fn lower_late_bound_vars(
436        &self,
437        genv: GlobalEnv,
438        def_id: LocalDefId,
439    ) -> QueryResult<List<ty::BoundVariableKind>> {
440        run_with_cache(&self.lower_late_bound_vars, def_id, || {
441            let hir_id = genv.tcx().local_def_id_to_hir_id(def_id);
442            genv.tcx()
443                .late_bound_vars(hir_id)
444                .lower(genv.tcx())
445                .map_err(|err| QueryErr::unsupported(def_id.to_def_id(), err.into_err()))
446        })
447    }
448
449    pub(crate) fn normalized_defns(
450        &self,
451        genv: GlobalEnv,
452        krate: CrateNum,
453    ) -> Rc<rty::NormalizedDefns> {
454        run_with_cache(&self.normalized_defns, krate, || {
455            if krate == LOCAL_CRATE {
456                Rc::new((self.providers.normalized_defns)(genv))
457            } else {
458                genv.cstore().normalized_defns(krate)
459            }
460        })
461    }
462
463    pub(crate) fn func_sort(&self, genv: GlobalEnv, def_id: FluxDefId) -> rty::PolyFuncSort {
464        run_with_cache(&self.func_sort, def_id, || {
465            def_id.dispatch_query(
466                genv,
467                |def_id| {
468                    // refinement functions cannot be extern specs so we simply grab the local id
469                    (self.providers.func_sort)(genv, def_id)
470                },
471                |def_id| genv.cstore().func_sort(def_id),
472                |_| {
473                    bug!(
474                        "cannot generate default function sort, the refinement must be defined somewhere"
475                    )
476                },
477            )
478        })
479    }
480
481    pub(crate) fn func_span(&self, genv: GlobalEnv, def_id: FluxDefId) -> Span {
482        run_with_cache(&self.func_span, def_id, || {
483            def_id.dispatch_query(
484                genv,
485                |def_id| {
486                    // refinement functions cannot be extern specs so we simply grab the local id
487                    (self.providers.func_span)(genv, def_id)
488                },
489                |def_id| genv.cstore().func_span(def_id),
490                |_|
491                bug!(
492                        "cannot generate default function sort, the refinement must be defined somewhere"
493                    )
494                ,
495            )
496        })
497    }
498
499    pub(crate) fn qualifiers(&self, genv: GlobalEnv) -> QueryResult<&[rty::Qualifier]> {
500        self.qualifiers
501            .get_or_init(|| (self.providers.qualifiers)(genv))
502            .as_deref()
503            .map_err(Clone::clone)
504    }
505
506    pub(crate) fn prim_rel(
507        &self,
508        genv: GlobalEnv,
509    ) -> QueryResult<&UnordMap<rty::BinOp, rty::PrimRel>> {
510        self.prim_rel
511            .get_or_init(|| (self.providers.prim_rel)(genv))
512            .as_ref()
513            .map_err(|err| err.clone())
514    }
515
516    pub(crate) fn adt_sort_def_of(
517        &self,
518        genv: GlobalEnv,
519        def_id: DefId,
520    ) -> QueryResult<rty::AdtSortDef> {
521        run_with_cache(&self.adt_sort_def_of, def_id, || {
522            def_id.dispatch_query(
523                genv,
524                |def_id| (self.providers.adt_sort_def_of)(genv, def_id),
525                |def_id| genv.cstore().adt_sort_def(def_id),
526                |def_id| {
527                    let variants = IndexVec::from([rty::AdtSortVariant::new(vec![])]);
528                    Ok(rty::AdtSortDef::new(def_id, vec![], variants, false, true))
529                },
530            )
531        })
532    }
533
534    pub(crate) fn sort_decl_param_count(&self, genv: GlobalEnv, def_id: FluxDefId) -> usize {
535        run_with_cache(&self.sort_decl_param_count, def_id, || {
536            def_id.dispatch_query(
537                genv,
538                |def_id| {
539                    (self.providers.sort_decl_param_count)(genv, def_id)
540                },
541                |def_id| genv.cstore().sort_decl_param_count(def_id),
542                |_| {
543                    bug!(
544                        "cannot generate default param count for sort declaration, it must be defined somewhere"
545                    )
546                }
547            )
548        })
549    }
550
551    pub(crate) fn check_wf(
552        &self,
553        genv: GlobalEnv<'genv, '_>,
554        def_id: LocalDefId,
555    ) -> QueryResult<Rc<rty::WfckResults>> {
556        run_with_cache(&self.check_wf, def_id, || (self.providers.check_wf)(genv, def_id))
557    }
558
559    pub(crate) fn constant_info(
560        &self,
561        genv: GlobalEnv,
562        def_id: DefId,
563    ) -> QueryResult<rty::ConstantInfo> {
564        run_with_cache(&self.constant_info, def_id, || {
565            def_id.dispatch_query(
566                genv,
567                |def_id| (self.providers.constant_info)(genv, def_id),
568                |def_id| genv.cstore().constant_info(def_id),
569                |def_id| {
570                    // TODO(RJ): fix duplication with [`conv_constant`]` in `flux-fhir-analysis`
571                    let ty = genv.tcx().type_of(def_id).no_bound_vars().unwrap();
572                    if ty.is_integral() {
573                        let val = genv.tcx().const_eval_poly(def_id).ok().and_then(|val| {
574                            let val = val.try_to_scalar_int()?;
575                            rty::Constant::from_scalar_int(genv.tcx(), val, &ty)
576                        });
577                        if let Some(constant_) = val {
578                            return Ok(rty::ConstantInfo::Interpreted(
579                                rty::Expr::constant(constant_),
580                                rty::Sort::Int,
581                            ));
582                        }
583                    }
584                    Ok(rty::ConstantInfo::Uninterpreted)
585                },
586            )
587        })
588    }
589
590    pub(crate) fn no_panic(&self, genv: GlobalEnv, def_id: DefId) -> bool {
591        run_with_cache(&self.no_panic, def_id, || {
592            def_id.dispatch_query(
593                genv,
594                |def_id| {
595                    let mut current_id = def_id.local_id();
596
597                    // Walk up the entire parent chain within this closure
598                    loop {
599                        // Skip dummy items
600                        if genv.is_dummy(current_id) {
601                            if let Some(parent) = genv.tcx().opt_local_parent(current_id) {
602                                current_id = parent;
603                                continue;
604                            } else {
605                                return false; // Reached top without finding non-dummy
606                            }
607                        }
608
609                        // Check if current non-dummy item has the `no_panic` attribute
610                        if genv.fhir_attr_map(current_id).no_panic() {
611                            return true;
612                        }
613
614                        // Move to the next parent
615                        if let Some(parent) = genv.tcx().opt_local_parent(current_id) {
616                            current_id = parent;
617                        } else {
618                            break; // Reached the top
619                        }
620                    }
621
622                    config::no_panic()
623                },
624                |def_id| genv.cstore().no_panic(def_id),
625                |_| false,
626            )
627        })
628    }
629
630    pub(crate) fn adt_def(&self, genv: GlobalEnv, def_id: DefId) -> QueryResult<rty::AdtDef> {
631        run_with_cache(&self.adt_def, def_id, || {
632            def_id.dispatch_query(
633                genv,
634                |def_id| (self.providers.adt_def)(genv, def_id),
635                |def_id| genv.cstore().adt_def(def_id),
636                |def_id| {
637                    let adt_def = genv.tcx().adt_def(def_id).lower(genv.tcx());
638                    Ok(rty::AdtDef::new(adt_def, genv.adt_sort_def_of(def_id)?, vec![], false))
639                },
640            )
641        })
642    }
643
644    pub(crate) fn generics_of(&self, genv: GlobalEnv, def_id: DefId) -> QueryResult<rty::Generics> {
645        run_with_cache(&self.generics_of, def_id, || {
646            def_id.dispatch_query(
647                genv,
648                |def_id| (self.providers.generics_of)(genv, def_id),
649                |def_id| genv.cstore().generics_of(def_id),
650                |def_id| {
651                    Ok(refining::refine_generics(genv, def_id, &genv.lower_generics_of(def_id)))
652                },
653            )
654        })
655    }
656
657    pub(crate) fn refinement_generics_of(
658        &self,
659        genv: GlobalEnv,
660        def_id: DefId,
661    ) -> QueryResult<rty::EarlyBinder<rty::RefinementGenerics>> {
662        run_with_cache(&self.refinement_generics_of, def_id, || {
663            def_id.dispatch_query(
664                genv,
665                |def_id| (self.providers.refinement_generics_of)(genv, def_id),
666                |def_id| genv.cstore().refinement_generics_of(def_id),
667                |def_id| {
668                    let parent = genv.tcx().generics_of(def_id).parent;
669                    Ok(rty::EarlyBinder(rty::RefinementGenerics {
670                        parent,
671                        parent_count: 0,
672                        own_params: List::empty(),
673                    }))
674                },
675            )
676        })
677    }
678
679    pub(crate) fn item_bounds(
680        &self,
681        genv: GlobalEnv<'genv, 'tcx>,
682        def_id: DefId,
683    ) -> QueryResult<rty::EarlyBinder<List<rty::Clause>>> {
684        run_with_cache(&self.item_bounds, def_id, || {
685            def_id.dispatch_query(
686                genv,
687                |def_id| (self.providers.item_bounds)(genv, def_id),
688                |def_id| genv.cstore().item_bounds(def_id),
689                |def_id| {
690                    let clauses = genv
691                        .tcx()
692                        .item_bounds(def_id)
693                        .skip_binder()
694                        .lower(genv.tcx())
695                        .map_err(|err| QueryErr::unsupported(def_id, err))?
696                        .refine(&Refiner::default_for_item(genv, def_id)?)?;
697
698                    Ok(rty::EarlyBinder(clauses))
699                },
700            )
701        })
702    }
703
704    pub(crate) fn predicates_of(
705        &self,
706        genv: GlobalEnv,
707        def_id: DefId,
708    ) -> QueryResult<rty::EarlyBinder<rty::GenericPredicates>> {
709        run_with_cache(&self.predicates_of, def_id, || {
710            def_id.dispatch_query(
711                genv,
712                |def_id| (self.providers.predicates_of)(genv, def_id),
713                |def_id| genv.cstore().predicates_of(def_id),
714                |def_id| {
715                    let predicates = genv
716                        .lower_predicates_of(def_id)?
717                        .refine(&Refiner::default_for_item(genv, def_id)?)?;
718                    Ok(rty::EarlyBinder(predicates))
719                },
720            )
721        })
722    }
723
724    pub(crate) fn assoc_refinements_of(
725        &self,
726        genv: GlobalEnv,
727        def_id: DefId,
728    ) -> QueryResult<rty::AssocRefinements> {
729        run_with_cache(&self.assoc_refinements_of, def_id, || {
730            def_id.dispatch_query(
731                genv,
732                |def_id| (self.providers.assoc_refinements_of)(genv, def_id),
733                |def_id| genv.cstore().assoc_refinements_of(def_id),
734                |def_id| Ok(genv.builtin_assoc_refts(def_id).unwrap_or_default()),
735            )
736        })
737    }
738
739    pub(crate) fn assoc_refinement_body(
740        &self,
741        genv: GlobalEnv,
742        impl_assoc_id: FluxDefId,
743    ) -> QueryResult<rty::EarlyBinder<rty::Lambda>> {
744        run_with_cache(&self.assoc_refinement_body, impl_assoc_id, || {
745            impl_assoc_id.dispatch_query(
746                genv,
747                |impl_assoc_id| (self.providers.assoc_refinement_body)(genv, impl_assoc_id),
748                |impl_assoc_id| genv.cstore().assoc_refinements_def(impl_assoc_id),
749                |impl_assoc_id| {
750                    Err(query_bug!(
751                        impl_assoc_id.parent(),
752                        "cannot generate default associate refinement for extern impl"
753                    ))
754                },
755            )
756        })
757    }
758
759    pub(crate) fn default_assoc_refinement_body(
760        &self,
761        genv: GlobalEnv,
762        trait_assoc_id: FluxDefId,
763    ) -> QueryResult<Option<rty::EarlyBinder<rty::Lambda>>> {
764        run_with_cache(&self.default_assoc_refinement_body, trait_assoc_id, || {
765            trait_assoc_id.dispatch_query(
766                genv,
767                |trait_assoc_id| {
768                    (self.providers.default_assoc_refinement_body)(genv, trait_assoc_id)
769                },
770                |trait_assoc_id| genv.cstore().default_assoc_refinements_def(trait_assoc_id),
771                |trait_assoc_id| {
772                    Err(query_bug!(
773                        trait_assoc_id.parent(),
774                        "cannot generate default assoc refinement for extern trait"
775                    ))
776                },
777            )
778        })
779    }
780
781    pub(crate) fn sort_of_assoc_reft(
782        &self,
783        genv: GlobalEnv,
784        assoc_id: FluxDefId,
785    ) -> QueryResult<rty::EarlyBinder<rty::FuncSort>> {
786        run_with_cache(&self.sort_of_assoc_reft, assoc_id, || {
787            assoc_id.dispatch_query(
788                genv,
789                |assoc_id| (self.providers.sort_of_assoc_reft)(genv, assoc_id),
790                |assoc_id| genv.cstore().sort_of_assoc_reft(assoc_id),
791                |assoc_id| {
792                    genv.builtin_assoc_reft_sort(assoc_id).ok_or_else(|| {
793                        query_bug!(
794                            assoc_id.parent(),
795                            "assoc refinement on extern crate is not builtin"
796                        )
797                    })
798                },
799            )
800        })
801    }
802
803    pub(crate) fn type_of(
804        &self,
805        genv: GlobalEnv,
806        def_id: DefId,
807    ) -> QueryResult<rty::EarlyBinder<rty::TyOrCtor>> {
808        run_with_cache(&self.type_of, def_id, || {
809            def_id.dispatch_query(
810                genv,
811                |def_id| (self.providers.type_of)(genv, def_id),
812                |def_id| genv.cstore().type_of(def_id),
813                |def_id| {
814                    // If we're given a type parameter, provide the generics of the parent container.
815                    let generics_def_id = match genv.def_kind(def_id) {
816                        DefKind::TyParam => genv.tcx().parent(def_id),
817                        _ => def_id,
818                    };
819                    let ty = genv.lower_type_of(def_id)?.skip_binder();
820                    Ok(rty::EarlyBinder(
821                        Refiner::default_for_item(genv, generics_def_id)?
822                            .refine_ty_or_base(&ty)?
823                            .into(),
824                    ))
825                },
826            )
827        })
828    }
829
830    pub(crate) fn variants_of(
831        &self,
832        genv: GlobalEnv,
833        def_id: DefId,
834    ) -> QueryResult<rty::Opaqueness<rty::EarlyBinder<rty::PolyVariants>>> {
835        run_with_cache(&self.variants_of, def_id, || {
836            def_id.dispatch_query(
837                genv,
838                |def_id| (self.providers.variants_of)(genv, def_id),
839                |def_id| genv.cstore().variants_of(def_id),
840                |def_id| {
841                    let variants = genv
842                        .tcx()
843                        .adt_def(def_id)
844                        .variants()
845                        .indices()
846                        .map(|variant_idx| {
847                            Refiner::default_for_item(genv, def_id)?
848                                .refine_variant_def(def_id, variant_idx)
849                        })
850                        .try_collect()?;
851                    Ok(rty::Opaqueness::Transparent(rty::EarlyBinder(variants)))
852                },
853            )
854        })
855    }
856
857    pub(crate) fn fn_sig(
858        &self,
859        genv: GlobalEnv,
860        def_id: DefId,
861    ) -> QueryResult<rty::EarlyBinder<rty::PolyFnSig>> {
862        run_with_cache(&self.fn_sig, def_id, || {
863            def_id.dispatch_query(
864                genv,
865                |def_id| (self.providers.fn_sig)(genv, def_id),
866                |def_id| genv.cstore().fn_sig(def_id),
867                |def_id| {
868                    let fn_sig = genv
869                        .lower_fn_sig(def_id)?
870                        .skip_binder()
871                        .refine(&Refiner::default_for_item(genv, def_id)?)?
872                        .hoist_input_binders();
873                    Ok(rty::EarlyBinder(fn_sig))
874                },
875            )
876        })
877    }
878}
879
880/// Logic to *dispatch* a `def_id` to a provider (`local`, `external`, or `default`).
881/// This is a trait so it can be implemented for [`DefId`] and for [`FluxDefId`].
882trait DispatchKey: Sized {
883    type LocalId;
884
885    fn dispatch_query<R>(
886        self,
887        genv: GlobalEnv,
888        local: impl FnOnce(Self::LocalId) -> R,
889        external: impl FnOnce(Self) -> Option<R>,
890        default: impl FnOnce(Self) -> R,
891    ) -> R;
892}
893
894impl DispatchKey for DefId {
895    type LocalId = MaybeExternId;
896
897    fn dispatch_query<R>(
898        self,
899        genv: GlobalEnv,
900        local: impl FnOnce(MaybeExternId) -> R,
901        external: impl FnOnce(Self) -> Option<R>,
902        default: impl FnOnce(Self) -> R,
903    ) -> R {
904        match genv.resolve_id(self) {
905            ResolvedDefId::Local(local_id) => {
906                // Case 1: `def_id` is a `LocalDefId` so forward it to the *local provider*
907                local(MaybeExternId::Local(local_id))
908            }
909            ResolvedDefId::ExternSpec(local_id, def_id) => {
910                // Case 2: `def_id` is a `LocalDefId` wrapping an extern spec, so we also
911                // forward it to the local provider
912                local(MaybeExternId::Extern(local_id, def_id))
913            }
914            ResolvedDefId::Extern(def_id) if let Some(v) = external(def_id) => {
915                // Case 3: `def_id` is an external `def_id` for which we have an annotation in the
916                // *external provider*
917                v
918            }
919            ResolvedDefId::Extern(def_id) => {
920                // Case 4: If none of the above, we generate a default annotation
921                default(def_id)
922            }
923        }
924    }
925}
926
927impl DispatchKey for FluxDefId {
928    type LocalId = FluxId<MaybeExternId>;
929
930    fn dispatch_query<R>(
931        self,
932        genv: GlobalEnv,
933        local: impl FnOnce(FluxId<MaybeExternId>) -> R,
934        external: impl FnOnce(FluxId<DefId>) -> Option<R>,
935        default: impl FnOnce(FluxId<DefId>) -> R,
936    ) -> R {
937        #[allow(
938            clippy::disallowed_methods,
939            reason = "we are mapping the parent id to a different representation which still guarantees the existence of the item"
940        )]
941        self.parent().dispatch_query(
942            genv,
943            |container_id| local(FluxId::new(container_id, self.name())),
944            |container_id| external(FluxId::new(container_id, self.name())),
945            |container_id| default(FluxId::new(container_id, self.name())),
946        )
947    }
948}
949
950fn run_with_cache<K, V>(cache: &Cache<K, V>, key: K, f: impl FnOnce() -> V) -> V
951where
952    K: std::hash::Hash + Eq,
953    V: Clone,
954{
955    if let Some(v) = cache.borrow().get(&key) {
956        return v.clone();
957    }
958    let v = f();
959    cache.borrow_mut().insert(key, v.clone());
960    v
961}
962
963impl<'a> Diagnostic<'a> for QueryErr {
964    #[track_caller]
965    fn into_diag(
966        self,
967        dcx: rustc_errors::DiagCtxtHandle<'a>,
968        _level: rustc_errors::Level,
969    ) -> rustc_errors::Diag<'a, ErrorGuaranteed> {
970        use crate::fluent_generated as fluent;
971
972        rustc_middle::ty::tls::with_opt(
973            #[track_caller]
974            |tcx| {
975                let tcx = tcx.expect("no TyCtxt stored in tls");
976                match self {
977                    QueryErr::Unsupported { def_id, err } => {
978                        let span = err.span.unwrap_or_else(|| tcx.def_span(def_id));
979                        let mut diag = dcx.struct_span_err(span, fluent::middle_query_unsupported);
980                        diag.code(E0999);
981                        diag.note(err.descr);
982                        diag
983                    }
984                    QueryErr::Ignored { def_id } => {
985                        let def_span = tcx.def_span(def_id);
986                        let mut diag =
987                            dcx.struct_span_err(def_span, fluent::middle_query_ignored_item);
988                        diag.code(E0999);
989                        diag
990                    }
991                    QueryErr::InvalidGenericArg { def_id } => {
992                        let def_span = tcx.def_span(def_id);
993                        let mut diag =
994                            dcx.struct_span_err(def_span, fluent::middle_query_invalid_generic_arg);
995                        diag.code(E0999);
996                        diag
997                    }
998                    QueryErr::MissingAssocReft { impl_id, name, .. } => {
999                        let def_span = tcx.def_span(impl_id);
1000                        let mut diag =
1001                            dcx.struct_span_err(def_span, fluent::middle_query_missing_assoc_reft);
1002                        diag.arg("name", name);
1003                        diag.code(E0999);
1004                        diag
1005                    }
1006                    QueryErr::Bug { def_id, location, msg } => {
1007                        let mut diag = dcx.struct_err(fluent::middle_query_bug);
1008                        if let Some(def_id) = def_id {
1009                            diag.span(tcx.def_span(def_id));
1010                        }
1011                        diag.arg("location", location);
1012                        diag.note(msg);
1013                        diag
1014                    }
1015                    QueryErr::Emitted(_) => {
1016                        let mut diag = dcx.struct_err("QueryErr::Emitted should be emitted");
1017                        diag.downgrade_to_delayed_bug();
1018                        diag
1019                    }
1020                    QueryErr::OpaqueStruct { struct_id } => {
1021                        let struct_span = tcx.def_span(struct_id);
1022                        let mut diag =
1023                            dcx.struct_span_err(struct_span, fluent::middle_query_opaque_struct);
1024                        diag.arg("struct", tcx.def_path_str(struct_id));
1025                        diag
1026                    }
1027                }
1028            },
1029        )
1030    }
1031}
1032
1033impl<'a> Diagnostic<'a> for QueryErrAt {
1034    #[track_caller]
1035    fn into_diag(
1036        self,
1037        dcx: rustc_errors::DiagCtxtHandle<'a>,
1038        level: rustc_errors::Level,
1039    ) -> rustc_errors::Diag<'a, ErrorGuaranteed> {
1040        use crate::fluent_generated as fluent;
1041
1042        rustc_middle::ty::tls::with_opt(
1043            #[track_caller]
1044            |tcx| {
1045                let tcx = tcx.expect("no TyCtxt stored in tls");
1046                let cx_span = self.cx.span();
1047                let mut diag = match self.err {
1048                    QueryErr::Unsupported { def_id, err, .. } => {
1049                        let mut diag =
1050                            dcx.struct_span_err(cx_span, fluent::middle_query_unsupported_at);
1051                        diag.arg("kind", tcx.def_kind(def_id).descr(def_id));
1052                        if let Some(def_ident_span) = tcx.def_ident_span(def_id) {
1053                            diag.span_note(def_ident_span, fluent::_subdiag::note);
1054                        }
1055                        diag.note(err.descr);
1056                        diag
1057                    }
1058                    QueryErr::Ignored { def_id } => {
1059                        let mut diag =
1060                            dcx.struct_span_err(cx_span, fluent::middle_query_ignored_at);
1061                        diag.arg("kind", tcx.def_kind(def_id).descr(def_id));
1062                        diag.arg("name", def_id_to_string(def_id));
1063                        diag.span_label(cx_span, fluent::_subdiag::label);
1064                        diag
1065                    }
1066                    QueryErr::MissingAssocReft { name, .. } => {
1067                        let mut diag = dcx
1068                            .struct_span_err(cx_span, fluent::middle_query_missing_assoc_reft_at);
1069                        diag.arg("name", name);
1070                        diag.code(E0999);
1071                        diag
1072                    }
1073                    QueryErr::OpaqueStruct { struct_id } => {
1074                        let mut diag =
1075                            dcx.struct_span_err(cx_span, fluent::middle_query_opaque_struct);
1076                        diag.arg("struct", tcx.def_path_str(struct_id));
1077                        diag.span_label(cx_span, fluent::_subdiag::label);
1078                        if let ErrCtxt::FnCheck(_, fn_def_id) = self.cx {
1079                            let fn_span = tcx.def_span(fn_def_id);
1080                            diag.arg("def_kind", tcx.def_descr(fn_def_id.to_def_id()));
1081                            diag.span_label(fn_span, fluent::middle_query_opaque_struct_help);
1082                            diag.note(fluent::middle_query_opaque_struct_note);
1083                        }
1084                        diag
1085                    }
1086                    QueryErr::InvalidGenericArg { .. }
1087                    | QueryErr::Emitted(_)
1088                    | QueryErr::Bug { .. } => {
1089                        let mut diag = self.err.into_diag(dcx, level);
1090                        diag.span(cx_span);
1091                        diag
1092                    }
1093                };
1094                diag.code(E0999);
1095                diag
1096            },
1097        )
1098    }
1099}
1100
1101impl From<ErrorGuaranteed> for QueryErr {
1102    fn from(err: ErrorGuaranteed) -> Self {
1103        Self::Emitted(err)
1104    }
1105}
1106
1107pub fn try_query<T>(f: impl FnOnce() -> QueryResult<T>) -> QueryResult<T> {
1108    f()
1109}