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