flux_middle/
global_env.rs

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