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