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