flux_middle/
global_env.rs

1use std::{alloc, path::PathBuf, ptr, rc::Rc, slice};
2
3use flux_arc_interner::List;
4use flux_common::{bug, result::ErrorEmitter};
5use flux_config as config;
6use flux_errors::FluxSession;
7use flux_rustc_bridge::{self, lowering::Lower, mir, ty};
8use flux_syntax::symbols::sym;
9use rustc_data_structures::unord::UnordSet;
10use rustc_hir::{
11    LangItem,
12    def::DefKind,
13    def_id::{CrateNum, DefId, LocalDefId},
14};
15use rustc_middle::{
16    query::IntoQueryParam,
17    ty::{TyCtxt, Variance},
18};
19use rustc_span::Span;
20pub use rustc_span::{Symbol, symbol::Ident};
21use tempfile::TempDir;
22
23use crate::{
24    cstore::CrateStoreDyn,
25    def_id::{FluxDefId, FluxLocalDefId, MaybeExternId, ResolvedDefId},
26    fhir::{self, VariantIdx},
27    queries::{DispatchKey, Providers, Queries, QueryErr, QueryResult},
28    query_bug,
29    rty::{
30        self, QualifierKind,
31        refining::{Refine as _, Refiner},
32    },
33};
34
35#[derive(Clone, Copy)]
36pub struct GlobalEnv<'genv, 'tcx> {
37    inner: &'genv GlobalEnvInner<'genv, 'tcx>,
38}
39
40struct GlobalEnvInner<'genv, 'tcx> {
41    tcx: TyCtxt<'tcx>,
42    sess: &'genv FluxSession,
43    arena: &'genv fhir::Arena,
44    cstore: Box<CrateStoreDyn>,
45    queries: Queries<'genv, 'tcx>,
46    tempdir: TempDir,
47}
48
49impl<'tcx> GlobalEnv<'_, 'tcx> {
50    pub fn enter<'a, R>(
51        tcx: TyCtxt<'tcx>,
52        sess: &'a FluxSession,
53        cstore: Box<CrateStoreDyn>,
54        arena: &'a fhir::Arena,
55        providers: Providers,
56        f: impl for<'genv> FnOnce(GlobalEnv<'genv, 'tcx>) -> R,
57    ) -> R {
58        // The tempdir must be in the same partition as the target directory so we can `fs::rename`
59        // files in it.
60        let tempdir = TempDir::new_in(lean_parent_dir(tcx)).unwrap();
61        let queries = Queries::new(providers);
62        let inner = GlobalEnvInner { tcx, sess, cstore, arena, queries, tempdir };
63        f(GlobalEnv { inner: &inner })
64    }
65}
66
67impl<'genv, 'tcx> GlobalEnv<'genv, 'tcx> {
68    pub fn queried(self, def_id: DefId) -> bool {
69        self.inner.queries.queried(def_id)
70    }
71
72    /// Runs a query only if the given key's `DefId` was previously queried during checking.
73    ///
74    /// During checking, we track all items transitively reached from explicitly included items.
75    /// This method is used during metadata encoding to avoid triggering queries for items that
76    /// were not reached. If the item was not previously queried, returns [`QueryErr::Ignored`].
77    pub fn run_query_if_reached<K: DispatchKey, R>(
78        self,
79        key: K,
80        query: impl FnOnce(Self, K) -> QueryResult<R>,
81    ) -> QueryResult<R> {
82        if !self.inner.queries.queried(key.def_id()) {
83            return Err(QueryErr::NotIncluded { def_id: key.def_id() });
84        }
85
86        query(self, key)
87    }
88
89    pub fn tcx(self) -> TyCtxt<'tcx> {
90        self.inner.tcx
91    }
92
93    pub fn sess(self) -> &'genv FluxSession {
94        self.inner.sess
95    }
96
97    pub fn collect_specs(self) -> &'genv crate::Specs {
98        self.inner.queries.collect_specs(self)
99    }
100
101    pub fn resolve_crate(self) -> &'genv crate::ResolverOutput {
102        self.inner.queries.resolve_crate(self)
103    }
104
105    /// Parent directory of the Lean project.
106    pub fn lean_parent_dir(self) -> PathBuf {
107        lean_parent_dir(self.tcx())
108    }
109
110    pub fn temp_dir(self) -> &'genv TempDir {
111        &self.inner.tempdir
112    }
113
114    pub fn desugar(self, def_id: LocalDefId) -> QueryResult<fhir::Node<'genv>> {
115        self.inner.queries.desugar(self, def_id)
116    }
117
118    pub fn fhir_attr_map(self, def_id: LocalDefId) -> fhir::AttrMap<'genv> {
119        self.inner.queries.fhir_attr_map(self, def_id)
120    }
121
122    pub fn fhir_crate(self) -> &'genv fhir::FluxItems<'genv> {
123        self.inner.queries.fhir_crate(self)
124    }
125
126    pub fn alloc<T>(&self, val: T) -> &'genv T {
127        self.inner.arena.alloc(val)
128    }
129
130    pub fn alloc_slice<T: Copy>(self, slice: &[T]) -> &'genv [T] {
131        self.inner.arena.alloc_slice_copy(slice)
132    }
133
134    pub fn alloc_slice_fill_iter<T, I>(self, it: I) -> &'genv [T]
135    where
136        I: IntoIterator<Item = T>,
137        I::IntoIter: ExactSizeIterator,
138    {
139        self.inner.arena.alloc_slice_fill_iter(it)
140    }
141
142    pub fn def_kind(&self, def_id: impl IntoQueryParam<DefId>) -> DefKind {
143        self.tcx().def_kind(def_id.into_query_param())
144    }
145
146    /// Allocates space to store `cap` elements of type `T`.
147    ///
148    /// The elements are initialized using the supplied iterator. At most `cap` elements will be
149    /// retrieved from the iterator. If the iterator yields fewer than `cap` elements, the returned
150    /// slice will be of length less than the allocated capacity.
151    ///
152    /// ## Panics
153    ///
154    /// Panics if reserving space for the slice fails.
155    pub fn alloc_slice_with_capacity<T, I>(self, cap: usize, it: I) -> &'genv [T]
156    where
157        I: IntoIterator<Item = T>,
158    {
159        let layout = alloc::Layout::array::<T>(cap).unwrap_or_else(|_| bug!("out of memory"));
160        let dst = self.inner.arena.alloc_layout(layout).cast::<T>();
161        unsafe {
162            let mut len = 0;
163            for (i, v) in it.into_iter().take(cap).enumerate() {
164                len += 1;
165                ptr::write(dst.as_ptr().add(i), v);
166            }
167
168            slice::from_raw_parts(dst.as_ptr(), len)
169        }
170    }
171
172    pub fn inlined_body(self, did: FluxDefId) -> rty::Binder<rty::Expr> {
173        self.normalized_defns(did.krate()).inlined_body(did)
174    }
175
176    pub fn normalized_info(self, did: FluxDefId) -> rty::FuncInfo {
177        self.normalized_defns(did.krate()).func_info(did).clone()
178    }
179
180    pub fn normalized_defns(self, krate: CrateNum) -> Rc<rty::NormalizedDefns> {
181        self.inner.queries.normalized_defns(self, krate)
182    }
183
184    pub fn prim_rel_for(self, op: &rty::BinOp) -> QueryResult<Option<&'genv rty::PrimRel>> {
185        Ok(self.inner.queries.prim_rel(self)?.get(op))
186    }
187
188    pub fn qualifiers(self) -> QueryResult<&'genv [rty::Qualifier]> {
189        self.inner.queries.qualifiers(self)
190    }
191
192    /// Return all the qualifiers that apply to an item, including both global and local qualifiers.
193    pub fn qualifiers_for(
194        self,
195        did: LocalDefId,
196    ) -> QueryResult<impl Iterator<Item = &'genv rty::Qualifier>> {
197        let quals = self.fhir_attr_map(did).qualifiers;
198        let names: UnordSet<_> = quals.iter().copied().collect();
199        Ok(self.qualifiers()?.iter().filter(move |qual| {
200            match qual.kind {
201                QualifierKind::Global => true,
202                QualifierKind::Hint => qual.def_id.parent() == did,
203                QualifierKind::Local => names.contains(&qual.def_id),
204            }
205        }))
206    }
207
208    /// Return the list of flux function definitions that should be revelaed for item
209    pub fn reveals_for(self, did: LocalDefId) -> &'genv [FluxDefId] {
210        self.fhir_attr_map(did).reveals
211    }
212
213    pub fn func_sort(self, def_id: impl IntoQueryParam<FluxDefId>) -> rty::PolyFuncSort {
214        self.inner
215            .queries
216            .func_sort(self, def_id.into_query_param())
217    }
218
219    pub fn func_span(self, def_id: impl IntoQueryParam<FluxDefId>) -> Span {
220        self.inner
221            .queries
222            .func_span(self, def_id.into_query_param())
223    }
224
225    pub fn should_inline_fun(self, def_id: FluxDefId) -> bool {
226        let is_poly = self.func_sort(def_id).params().len() > 0;
227        is_poly || !flux_config::smt_define_fun()
228    }
229
230    pub fn variances_of(self, did: DefId) -> &'tcx [Variance] {
231        self.tcx().variances_of(did)
232    }
233
234    pub fn mir(self, def_id: LocalDefId) -> QueryResult<Rc<mir::BodyRoot<'tcx>>> {
235        self.inner.queries.mir(self, def_id)
236    }
237
238    pub fn lower_generics_of(self, def_id: impl IntoQueryParam<DefId>) -> ty::Generics<'tcx> {
239        self.inner
240            .queries
241            .lower_generics_of(self, def_id.into_query_param())
242    }
243
244    pub fn lower_predicates_of(
245        self,
246        def_id: impl IntoQueryParam<DefId>,
247    ) -> QueryResult<ty::GenericPredicates> {
248        self.inner
249            .queries
250            .lower_predicates_of(self, def_id.into_query_param())
251    }
252
253    pub fn lower_type_of(
254        self,
255        def_id: impl IntoQueryParam<DefId>,
256    ) -> QueryResult<ty::EarlyBinder<ty::Ty>> {
257        self.inner
258            .queries
259            .lower_type_of(self, def_id.into_query_param())
260    }
261
262    pub fn lower_fn_sig(
263        self,
264        def_id: impl Into<DefId>,
265    ) -> QueryResult<ty::EarlyBinder<ty::PolyFnSig>> {
266        self.inner.queries.lower_fn_sig(self, def_id.into())
267    }
268
269    pub fn adt_def(self, def_id: impl IntoQueryParam<DefId>) -> QueryResult<rty::AdtDef> {
270        self.inner.queries.adt_def(self, def_id.into_query_param())
271    }
272
273    pub fn constant_info(
274        self,
275        def_id: impl IntoQueryParam<DefId>,
276    ) -> QueryResult<rty::ConstantInfo> {
277        self.inner
278            .queries
279            .constant_info(self, def_id.into_query_param())
280    }
281
282    pub fn static_info(self, def_id: impl IntoQueryParam<DefId>) -> QueryResult<rty::StaticInfo> {
283        self.inner
284            .queries
285            .static_info(self, def_id.into_query_param())
286    }
287
288    pub fn adt_sort_def_of(
289        self,
290        def_id: impl IntoQueryParam<DefId>,
291    ) -> QueryResult<rty::AdtSortDef> {
292        self.inner
293            .queries
294            .adt_sort_def_of(self, def_id.into_query_param())
295    }
296
297    pub fn sort_decl_param_count(self, def_id: impl IntoQueryParam<FluxDefId>) -> usize {
298        self.inner
299            .queries
300            .sort_decl_param_count(self, def_id.into_query_param())
301    }
302
303    pub fn check_wf(self, def_id: LocalDefId) -> QueryResult<Rc<rty::WfckResults>> {
304        self.inner.queries.check_wf(self, def_id)
305    }
306
307    pub fn impl_trait_ref(self, impl_id: DefId) -> QueryResult<rty::EarlyBinder<rty::TraitRef>> {
308        let trait_ref = self.tcx().impl_trait_ref(impl_id);
309        let trait_ref = trait_ref.skip_binder();
310        let trait_ref = trait_ref
311            .lower(self.tcx())
312            .map_err(|err| QueryErr::unsupported(impl_id, err.into_err()))?
313            .refine(&Refiner::default_for_item(self, impl_id)?)?;
314        Ok(rty::EarlyBinder(trait_ref))
315    }
316
317    pub fn generics_of(self, def_id: impl IntoQueryParam<DefId>) -> QueryResult<rty::Generics> {
318        self.inner
319            .queries
320            .generics_of(self, def_id.into_query_param())
321    }
322
323    pub fn refinement_generics_of(
324        self,
325        def_id: impl IntoQueryParam<DefId>,
326    ) -> QueryResult<rty::EarlyBinder<rty::RefinementGenerics>> {
327        self.inner
328            .queries
329            .refinement_generics_of(self, def_id.into_query_param())
330    }
331
332    pub fn predicates_of(
333        self,
334        def_id: impl IntoQueryParam<DefId>,
335    ) -> QueryResult<rty::EarlyBinder<rty::GenericPredicates>> {
336        self.inner
337            .queries
338            .predicates_of(self, def_id.into_query_param())
339    }
340
341    pub fn assoc_refinements_of(
342        self,
343        def_id: impl IntoQueryParam<DefId>,
344    ) -> QueryResult<rty::AssocRefinements> {
345        self.inner
346            .queries
347            .assoc_refinements_of(self, def_id.into_query_param())
348    }
349
350    pub fn assoc_refinement(self, assoc_id: FluxDefId) -> QueryResult<rty::AssocReft> {
351        Ok(self.assoc_refinements_of(assoc_id.parent())?.get(assoc_id))
352    }
353
354    /// Given the id of an associated refinement in a trait definition returns the body for the
355    /// corresponding associated refinement in the implementation with id `impl_id`.
356    ///
357    /// This function returns [`QueryErr::MissingAssocReft`] if the associated refinement is not
358    /// found in the implementation and there's no default body in the trait. This can happen if an
359    /// extern spec adds an associated refinement without a default body because we are currently
360    /// not checking `compare_impl_item` for those definitions.
361    pub fn assoc_refinement_body_for_impl(
362        self,
363        trait_assoc_id: FluxDefId,
364        impl_id: DefId,
365    ) -> QueryResult<rty::EarlyBinder<rty::Lambda>> {
366        // Check if the implementation has the associated refinement
367        let impl_assoc_refts = self.assoc_refinements_of(impl_id)?;
368        if let Some(impl_assoc_reft) = impl_assoc_refts.find(trait_assoc_id.name()) {
369            return self.assoc_refinement_body(impl_assoc_reft.def_id());
370        }
371
372        // Otherwise, check if the trait has a default body
373        if let Some(body) = self.default_assoc_refinement_body(trait_assoc_id)? {
374            let impl_trait_ref = self.impl_trait_ref(impl_id)?.instantiate_identity();
375            return Ok(rty::EarlyBinder(body.instantiate(self.tcx(), &impl_trait_ref.args, &[])));
376        }
377
378        Err(QueryErr::MissingAssocReft {
379            impl_id,
380            trait_id: trait_assoc_id.parent(),
381            name: trait_assoc_id.name(),
382        })
383    }
384
385    pub fn default_assoc_refinement_body(
386        self,
387        trait_assoc_id: FluxDefId,
388    ) -> QueryResult<Option<rty::EarlyBinder<rty::Lambda>>> {
389        self.inner
390            .queries
391            .default_assoc_refinement_body(self, trait_assoc_id)
392    }
393
394    pub fn assoc_refinement_body(
395        self,
396        impl_assoc_id: FluxDefId,
397    ) -> QueryResult<rty::EarlyBinder<rty::Lambda>> {
398        self.inner
399            .queries
400            .assoc_refinement_body(self, impl_assoc_id)
401    }
402
403    pub fn sort_of_assoc_reft(
404        self,
405        assoc_id: FluxDefId,
406    ) -> QueryResult<rty::EarlyBinder<rty::FuncSort>> {
407        self.inner.queries.sort_of_assoc_reft(self, assoc_id)
408    }
409
410    pub fn item_bounds(
411        self,
412        def_id: impl IntoQueryParam<DefId>,
413    ) -> QueryResult<rty::EarlyBinder<List<rty::Clause>>> {
414        self.inner
415            .queries
416            .item_bounds(self, def_id.into_query_param())
417    }
418
419    pub fn type_of(
420        self,
421        def_id: impl IntoQueryParam<DefId>,
422    ) -> QueryResult<rty::EarlyBinder<rty::TyOrCtor>> {
423        self.inner.queries.type_of(self, def_id.into_query_param())
424    }
425
426    pub fn fn_sig(
427        self,
428        def_id: impl IntoQueryParam<DefId>,
429    ) -> QueryResult<rty::EarlyBinder<rty::PolyFnSig>> {
430        self.inner.queries.fn_sig(self, def_id.into_query_param())
431    }
432
433    pub fn variants_of(
434        self,
435        def_id: impl IntoQueryParam<DefId>,
436    ) -> QueryResult<rty::Opaqueness<rty::EarlyBinder<rty::PolyVariants>>> {
437        self.inner
438            .queries
439            .variants_of(self, def_id.into_query_param())
440    }
441
442    pub fn variant_sig(
443        self,
444        def_id: DefId,
445        variant_idx: VariantIdx,
446    ) -> QueryResult<rty::Opaqueness<rty::EarlyBinder<rty::PolyVariant>>> {
447        Ok(self
448            .variants_of(def_id)?
449            .map(|variants| variants.map(|variants| variants[variant_idx.as_usize()].clone())))
450    }
451
452    pub fn lower_late_bound_vars(
453        self,
454        def_id: LocalDefId,
455    ) -> QueryResult<List<ty::BoundVariableKind>> {
456        self.inner.queries.lower_late_bound_vars(self, def_id)
457    }
458
459    /// Whether the function is marked with `#[flux::no_panic]`
460    pub fn no_panic(self, def_id: impl IntoQueryParam<DefId>) -> bool {
461        self.inner.queries.no_panic(self, def_id.into_query_param())
462    }
463
464    pub fn is_box(&self, res: fhir::Res) -> bool {
465        res.is_box(self.tcx())
466    }
467
468    pub fn def_id_to_param_index(&self, def_id: DefId) -> u32 {
469        let parent = self.tcx().parent(def_id);
470        let generics = self.tcx().generics_of(parent);
471        generics.param_def_id_to_index(self.tcx(), def_id).unwrap()
472    }
473
474    pub(crate) fn cstore(self) -> &'genv CrateStoreDyn {
475        &*self.inner.cstore
476    }
477
478    pub fn has_trusted_impl(&self, def_id: DefId) -> bool {
479        if let Some(did) = self
480            .resolve_id(def_id)
481            .as_maybe_extern()
482            .map(|id| id.local_id())
483        {
484            self.trusted_impl(did)
485        } else {
486            false
487        }
488    }
489
490    /// The `Output` associated type is defined in `FnOnce`, and `Fn`/`FnMut`
491    /// inherit it, so this should suffice to check if the `def_id`
492    /// corresponds to `LangItem::FnOnceOutput`.
493    pub fn is_fn_output(&self, def_id: DefId) -> bool {
494        let def_span = self.tcx().def_span(def_id);
495        self.tcx()
496            .require_lang_item(LangItem::FnOnceOutput, def_span)
497            == def_id
498    }
499
500    /// Returns whether `def_id` is the `call` method in the `Fn` trait.
501    pub fn is_fn_call(&self, def_id: DefId) -> bool {
502        let tcx = self.tcx();
503        let Some(assoc_item) = tcx.opt_associated_item(def_id) else { return false };
504        let Some(trait_id) = assoc_item.trait_container(tcx) else { return false };
505        assoc_item.name() == sym::call && tcx.is_lang_item(trait_id, LangItem::Fn)
506    }
507
508    /// Iterator over all local def ids that are not a extern spec
509    pub fn iter_local_def_id(self) -> impl Iterator<Item = LocalDefId> + use<'tcx, 'genv> {
510        self.tcx().iter_local_def_id().filter(move |&local_def_id| {
511            self.maybe_extern_id(local_def_id).is_local() && !self.is_dummy(local_def_id)
512        })
513    }
514
515    pub fn iter_extern_def_id(self) -> impl Iterator<Item = DefId> + use<'tcx, 'genv> {
516        self.tcx()
517            .iter_local_def_id()
518            .filter_map(move |local_def_id| self.maybe_extern_id(local_def_id).as_extern())
519    }
520
521    pub fn maybe_extern_id(self, local_id: LocalDefId) -> MaybeExternId {
522        self.collect_specs()
523            .local_id_to_extern_id
524            .get(&local_id)
525            .map_or_else(
526                || MaybeExternId::Local(local_id),
527                |def_id| MaybeExternId::Extern(local_id, *def_id),
528            )
529    }
530
531    #[expect(clippy::disallowed_methods)]
532    pub fn resolve_id(self, def_id: DefId) -> ResolvedDefId {
533        let maybe_extern_spec = self
534            .collect_specs()
535            .extern_id_to_local_id
536            .get(&def_id)
537            .copied();
538        if let Some(local_id) = maybe_extern_spec {
539            ResolvedDefId::ExternSpec(local_id, def_id)
540        } else if let Some(local_id) = def_id.as_local() {
541            debug_assert!(
542                self.maybe_extern_id(local_id).is_local(),
543                "def id points to dummy local item `{def_id:?}`"
544            );
545            ResolvedDefId::Local(local_id)
546        } else {
547            ResolvedDefId::Extern(def_id)
548        }
549    }
550
551    pub fn infer_opts(self, def_id: LocalDefId) -> config::InferOpts {
552        let mut opts = config::PartialInferOpts::default();
553        self.traverse_parents(def_id, |did| {
554            if let Some(o) = self.fhir_attr_map(did).infer_opts() {
555                opts.merge(&o);
556            }
557            None::<!>
558        });
559        opts.into()
560    }
561
562    /// Transitively follow the parent-chain of `def_id` to find the first containing item with an
563    /// explicit `#[flux::trusted(..)]` annotation and return whether that item is trusted or not.
564    /// If no explicit annotation is found, return `false`.
565    pub fn trusted(self, def_id: LocalDefId) -> bool {
566        self.traverse_parents(def_id, |did| self.fhir_attr_map(did).trusted())
567            .map(|trusted| trusted.to_bool())
568            .unwrap_or_else(config::trusted_default)
569    }
570
571    pub fn trusted_impl(self, def_id: LocalDefId) -> bool {
572        self.traverse_parents(def_id, |did| self.fhir_attr_map(did).trusted_impl())
573            .map(|trusted| trusted.to_bool())
574            .unwrap_or(false)
575    }
576
577    /// Whether the item is a dummy item created by the extern spec macro.
578    ///
579    /// See [`crate::Specs::dummy_extern`]
580    pub fn is_dummy(self, def_id: LocalDefId) -> bool {
581        self.traverse_parents(def_id, |did| {
582            self.collect_specs()
583                .dummy_extern
584                .contains(&did)
585                .then_some(())
586        })
587        .is_some()
588    }
589
590    /// Transitively follow the parent-chain of `def_id` to find the first containing item with an
591    /// explicit `#[flux::ignore(..)]` annotation and return whether that item is ignored or not.
592    /// If no explicit annotation is found, return `false`.
593    pub fn ignored(self, def_id: LocalDefId) -> bool {
594        self.traverse_parents(def_id, |did| self.fhir_attr_map(did).ignored())
595            .map(|ignored| ignored.to_bool())
596            .unwrap_or_else(config::ignore_default)
597    }
598
599    /// Whether the function is marked with `#[flux::should_fail]`
600    pub fn should_fail(self, def_id: LocalDefId) -> bool {
601        self.fhir_attr_map(def_id).should_fail()
602    }
603
604    /// Whether the function is marked with `#[proven_externally]`
605    pub fn proven_externally(self, def_id: LocalDefId) -> Option<Span> {
606        self.fhir_attr_map(def_id).proven_externally()
607    }
608
609    /// Traverse the parent chain of `def_id` until the first node for which `f` returns [`Some`].
610    fn traverse_parents<T>(
611        self,
612        mut def_id: LocalDefId,
613        mut f: impl FnMut(LocalDefId) -> Option<T>,
614    ) -> Option<T> {
615        loop {
616            if let Some(v) = f(def_id) {
617                break Some(v);
618            }
619
620            if let Some(parent) = self.tcx().opt_local_parent(def_id) {
621                def_id = parent;
622            } else {
623                break None;
624            }
625        }
626    }
627}
628
629impl<'genv, 'tcx> GlobalEnv<'genv, 'tcx> {
630    pub fn fhir_iter_flux_items(
631        self,
632    ) -> impl Iterator<Item = (FluxLocalDefId, fhir::FluxItem<'genv>)> {
633        self.fhir_crate()
634            .items
635            .iter()
636            .map(|(id, item)| (*id, *item))
637    }
638
639    pub fn fhir_sort_decl(&self, def_id: FluxLocalDefId) -> Option<&fhir::SortDecl> {
640        self.fhir_crate().items.get(&def_id).and_then(|item| {
641            if let fhir::FluxItem::SortDecl(sort_decl) = item { Some(*sort_decl) } else { None }
642        })
643    }
644
645    pub fn fhir_spec_func_body(
646        &self,
647        def_id: FluxLocalDefId,
648    ) -> Option<&'genv fhir::SpecFunc<'genv>> {
649        self.fhir_crate()
650            .items
651            .get(&def_id)
652            .and_then(|item| if let fhir::FluxItem::Func(defn) = item { Some(*defn) } else { None })
653    }
654
655    pub fn fhir_qualifiers(self) -> impl Iterator<Item = &'genv fhir::Qualifier<'genv>> {
656        self.fhir_crate().items.values().filter_map(|item| {
657            if let fhir::FluxItem::Qualifier(qual) = item { Some(*qual) } else { None }
658        })
659    }
660
661    pub fn fhir_primop_props(self) -> impl Iterator<Item = &'genv fhir::PrimOpProp<'genv>> {
662        self.fhir_crate().items.values().filter_map(|item| {
663            if let fhir::FluxItem::PrimOpProp(prop) = item { Some(*prop) } else { None }
664        })
665    }
666
667    pub fn fhir_get_generics(
668        self,
669        def_id: LocalDefId,
670    ) -> QueryResult<Option<&'genv fhir::Generics<'genv>>> {
671        // We don't have nodes for closures and coroutines
672        if matches!(self.def_kind(def_id), DefKind::Closure) {
673            Ok(None)
674        } else {
675            Ok(Some(self.fhir_expect_owner_node(def_id)?.generics()))
676        }
677    }
678
679    pub fn fhir_expect_refinement_kind(
680        self,
681        def_id: LocalDefId,
682    ) -> QueryResult<&'genv fhir::RefinementKind<'genv>> {
683        let kind = match &self.fhir_expect_item(def_id)?.kind {
684            fhir::ItemKind::Enum(enum_def) => &enum_def.refinement,
685            fhir::ItemKind::Struct(struct_def) => &struct_def.refinement,
686            _ => bug!("expected struct, enum or type alias"),
687        };
688        Ok(kind)
689    }
690
691    pub fn fhir_expect_item(self, def_id: LocalDefId) -> QueryResult<&'genv fhir::Item<'genv>> {
692        if let fhir::Node::Item(item) = self.fhir_node(def_id)? {
693            Ok(item)
694        } else {
695            Err(query_bug!(def_id, "expected item: `{def_id:?}`"))
696        }
697    }
698
699    pub fn fhir_expect_owner_node(self, def_id: LocalDefId) -> QueryResult<fhir::OwnerNode<'genv>> {
700        let Some(owner) = self.fhir_node(def_id)?.as_owner() else {
701            return Err(query_bug!(def_id, "cannot find owner node"));
702        };
703        Ok(owner)
704    }
705
706    pub fn fhir_node(self, def_id: LocalDefId) -> QueryResult<fhir::Node<'genv>> {
707        self.desugar(def_id)
708    }
709}
710
711#[macro_export]
712macro_rules! try_alloc_slice {
713    ($genv:expr, $slice:expr, $map:expr $(,)?) => {{
714        let slice = $slice;
715        $crate::try_alloc_slice!($genv, cap: slice.len(), slice.into_iter().map($map))
716    }};
717    ($genv:expr, cap: $cap:expr, $it:expr $(,)?) => {{
718        let mut err = None;
719        let slice = $genv.alloc_slice_with_capacity($cap, $it.into_iter().collect_errors(&mut err));
720        err.map_or(Ok(slice), Err)
721    }};
722}
723
724impl ErrorEmitter for GlobalEnv<'_, '_> {
725    fn emit<'a>(&'a self, err: impl rustc_errors::Diagnostic<'a>) -> rustc_span::ErrorGuaranteed {
726        self.sess().emit(err)
727    }
728}
729
730fn lean_parent_dir(tcx: TyCtxt) -> PathBuf {
731    tcx.sess
732        .opts
733        .working_dir
734        .local_path_if_available()
735        .to_path_buf()
736        .join(config::lean_dir())
737}