Skip to main content

flux_middle/
global_env.rs

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