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_attr_ir::LangItem;
17use rustc_data_structures::unord::{UnordMap, UnordSet};
18use rustc_hir::{
19    def::DefKind,
20    def_id::{CrateNum, DefId, LocalDefId},
21};
22use rustc_middle::{
23    query::IntoQueryKey,
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    /// Akin to `rustc_middle::ty::TyCtxt::module_children` but for flux items (`defs!` and
134    /// sort declarations) defined directly in a module.
135    pub fn flux_module_children(self, def_id: DefId) -> &'genv [fhir::FluxModChild] {
136        self.inner.queries.flux_module_children(self, def_id)
137    }
138
139    /// Parent directory of the Lean project.
140    pub fn lean_parent_dir(self) -> PathBuf {
141        lean_parent_dir(self.tcx())
142    }
143
144    pub fn temp_dir(self) -> &'genv TempDir {
145        &self.inner.tempdir
146    }
147
148    pub fn desugar(self, def_id: LocalDefId) -> QueryResult<fhir::Node<'genv>> {
149        self.inner.queries.desugar(self, def_id)
150    }
151
152    pub fn fhir_attr_map(self, def_id: LocalDefId) -> fhir::AttrMap<'genv> {
153        self.inner.queries.fhir_attr_map(self, def_id)
154    }
155
156    pub fn fhir_crate(self) -> &'genv fhir::FluxItems<'genv> {
157        self.inner.queries.fhir_crate(self)
158    }
159
160    pub fn alloc<T>(&self, val: T) -> &'genv T {
161        self.inner.arena.alloc(val)
162    }
163
164    pub fn alloc_slice<T: Copy>(self, slice: &[T]) -> &'genv [T] {
165        self.inner.arena.alloc_slice_copy(slice)
166    }
167
168    pub fn alloc_slice_fill_iter<T, I>(self, it: I) -> &'genv [T]
169    where
170        I: IntoIterator<Item = T>,
171        I::IntoIter: ExactSizeIterator,
172    {
173        self.inner.arena.alloc_slice_fill_iter(it)
174    }
175
176    pub fn def_kind(&self, def_id: impl IntoQueryKey<DefId>) -> DefKind {
177        self.tcx().def_kind(def_id.into_query_key())
178    }
179
180    /// Allocates space to store `cap` elements of type `T`.
181    ///
182    /// The elements are initialized using the supplied iterator. At most `cap` elements will be
183    /// retrieved from the iterator. If the iterator yields fewer than `cap` elements, the returned
184    /// slice will be of length less than the allocated capacity.
185    ///
186    /// ## Panics
187    ///
188    /// Panics if reserving space for the slice fails.
189    pub fn alloc_slice_with_capacity<T, I>(self, cap: usize, it: I) -> &'genv [T]
190    where
191        I: IntoIterator<Item = T>,
192    {
193        let layout = alloc::Layout::array::<T>(cap).unwrap_or_else(|_| bug!("out of memory"));
194        let dst = self.inner.arena.alloc_layout(layout).cast::<T>();
195        unsafe {
196            let mut len = 0;
197            for (i, v) in it.into_iter().take(cap).enumerate() {
198                len += 1;
199                ptr::write(dst.as_ptr().add(i), v);
200            }
201
202            slice::from_raw_parts(dst.as_ptr(), len)
203        }
204    }
205
206    pub fn call_graph(self) -> &'genv crate::call_graph::CallGraph<'tcx> {
207        self.inner.queries.call_graph(self)
208    }
209
210    /// The inferred [`PanicSpec`] for the node `key`, looked up in the local crate's
211    /// `NodeKey`-keyed map. Missing entries default to `MightPanic(NotInCallGraph)`.
212    pub fn inferred_no_panic_key(self, key: NodeKey<'tcx>) -> PanicSpec {
213        self.inferred_no_panic_local()
214            .get(&key)
215            .copied()
216            .unwrap_or(PanicSpec::MightPanic(PanicReason::NotInCallGraph))
217    }
218
219    /// The local crate's full `NodeKey`-keyed no-panic map (used by metadata encoding).
220    pub fn inferred_no_panic_local(self) -> Rc<UnordMap<NodeKey<'tcx>, PanicSpec>> {
221        self.inner.queries.inferred_no_panic(self)
222    }
223
224    /// Looks up the inferred [`PanicSpec`] for a node `key` defined in an *external* crate, from
225    /// that crate's serialized metadata table. Tries the exact key first (so a serialized
226    /// [`Mono`](NodeKey::Mono) is used when present), then for a monomorphization falls back to the
227    /// source [`Item`](NodeKey::Item) (covering instantiations the external crate could never have
228    /// analyzed, e.g. at a local type), then to `MightPanic`.
229    pub fn inferred_no_panic_external(self, key: NodeKey<'tcx>) -> PanicSpec {
230        let table = self.cstore().inferred_no_panic(key.def_id().krate);
231        if let Some(&spec) = table.get(&key) {
232            return spec;
233        }
234        if let NodeKey::Mono(instance) = key
235            && let Some(&spec) = table.get(&NodeKey::Item(instance.def_id()))
236        {
237            return spec;
238        }
239        PanicSpec::MightPanic(PanicReason::NotInCallGraph)
240    }
241
242    /// The body of a spec function after inlining all or some of the spec functions it
243    /// (transitively) calls. Whether a spec function is inlined is decided by
244    /// [`GlobalEnv::should_inline_fun`] in the current session.
245    pub fn inlined_body(self, did: FluxDefId) -> rty::Binder<rty::Expr> {
246        self.inner.queries.inlined_body(self, did)
247    }
248
249    pub fn spec_func(self, did: FluxDefId) -> rty::SpecFunc {
250        self.spec_funcs(did.krate()).get(did.index()).clone()
251    }
252
253    /// The rank of a spec function in the topological order of the spec functions of its crate
254    pub fn spec_func_rank(self, did: FluxDefId) -> usize {
255        self.spec_funcs(did.krate()).rank(did.index())
256    }
257
258    /// All the spec functions of a crate in topological order
259    pub fn spec_funcs(self, krate: CrateNum) -> Rc<rty::SpecFuncs> {
260        self.inner.queries.spec_funcs(self, krate)
261    }
262
263    pub fn prim_rel_for(self, op: &rty::BinOp) -> QueryResult<Option<&'genv rty::PrimRel>> {
264        Ok(self.inner.queries.prim_rel(self)?.get(op))
265    }
266
267    pub fn qualifiers(self) -> QueryResult<&'genv [rty::Qualifier]> {
268        self.inner.queries.qualifiers(self)
269    }
270
271    /// Return all the (global) axioms defined in the local crate.
272    pub fn axioms(self) -> QueryResult<&'genv [rty::Axiom]> {
273        self.inner.queries.axioms(self)
274    }
275
276    /// Return all the qualifiers that apply to an item, including both global and local qualifiers.
277    pub fn qualifiers_for(
278        self,
279        did: LocalDefId,
280    ) -> QueryResult<impl Iterator<Item = &'genv rty::Qualifier>> {
281        let quals = self.fhir_attr_map(did).qualifiers;
282        let names: UnordSet<_> = quals.iter().copied().collect();
283        Ok(self.qualifiers()?.iter().filter(move |qual| {
284            match qual.kind {
285                QualifierKind::Global => true,
286                QualifierKind::Hint => {
287                    self.flux_item_enclosing_item(qual.def_id.to_def_id()) == did.to_def_id()
288                }
289                QualifierKind::Local => names.contains(&qual.def_id),
290            }
291        }))
292    }
293
294    /// The item enclosing the declaration of a flux item. This is different from the
295    /// [`FluxDefId::parent`] for flux items declared with `defs!` that use the id of
296    /// the declaring `const _ = { }`.
297    fn flux_item_enclosing_item(self, def_id: FluxDefId) -> DefId {
298        let declarer = def_id.parent();
299        if self.def_kind(declarer) == DefKind::Const {
300            self.tcx().typeck_root_def_id(self.tcx().parent(declarer))
301        } else {
302            declarer
303        }
304    }
305
306    /// Return the list of flux function definitions that should be revelaed for item
307    pub fn reveals_for(self, did: LocalDefId) -> impl Iterator<Item = FluxDefId> + 'genv {
308        self.fhir_attr_map(did)
309            .reveals
310            .iter()
311            .map(|(def_id, _)| *def_id)
312    }
313
314    /// Return the list of flux function definitions that should be hidden for item
315    pub fn hides_for(self, did: LocalDefId) -> impl Iterator<Item = FluxDefId> + 'genv {
316        self.fhir_attr_map(did)
317            .hides
318            .iter()
319            .map(|(def_id, _)| *def_id)
320    }
321
322    pub fn func_sort(self, def_id: impl IntoQueryKey<FluxDefId>) -> rty::PolyFuncSort {
323        self.inner.queries.func_sort(self, def_id.into_query_key())
324    }
325
326    pub fn func_span(self, def_id: impl IntoQueryKey<FluxDefId>) -> Span {
327        self.inner.queries.func_span(self, def_id.into_query_key())
328    }
329
330    /// Whether a (non-hidden) flux-def should be replaced by its body instead of being
331    /// represented as a `define-fun`. We inline all *polymorphic* flux-defs, since they cannot be
332    /// represented as `define-fun` in SMTLIB, but leave all *monomorphic* flux-defs un-inlined.
333    pub fn should_inline_fun(self, def_id: FluxDefId) -> bool {
334        self.func_sort(def_id).params().len() > 0
335    }
336
337    pub fn variances_of(self, did: DefId) -> &'tcx [Variance] {
338        self.tcx().variances_of(did)
339    }
340
341    pub fn mir(self, def_id: LocalDefId) -> QueryResult<Rc<mir::BodyRoot<'tcx>>> {
342        self.inner.queries.mir(self, def_id)
343    }
344
345    pub fn lower_generics_of(self, def_id: impl IntoQueryKey<DefId>) -> ty::Generics<'tcx> {
346        self.inner
347            .queries
348            .lower_generics_of(self, def_id.into_query_key())
349    }
350
351    pub fn lower_predicates_of(
352        self,
353        def_id: impl IntoQueryKey<DefId>,
354    ) -> QueryResult<ty::GenericPredicates> {
355        self.inner
356            .queries
357            .lower_predicates_of(self, def_id.into_query_key())
358    }
359
360    pub fn lower_type_of(
361        self,
362        def_id: impl IntoQueryKey<DefId>,
363    ) -> QueryResult<ty::EarlyBinder<ty::Ty>> {
364        self.inner
365            .queries
366            .lower_type_of(self, def_id.into_query_key())
367    }
368
369    pub fn lower_fn_sig(
370        self,
371        def_id: impl Into<DefId>,
372    ) -> QueryResult<ty::EarlyBinder<ty::PolyFnSig>> {
373        self.inner.queries.lower_fn_sig(self, def_id.into())
374    }
375
376    pub fn adt_def(self, def_id: impl IntoQueryKey<DefId>) -> QueryResult<rty::AdtDef> {
377        self.inner.queries.adt_def(self, def_id.into_query_key())
378    }
379
380    pub fn invariants_of(
381        self,
382        def_id: impl IntoQueryKey<DefId>,
383    ) -> rty::EarlyBinder<List<rty::Invariant>> {
384        self.inner
385            .queries
386            .invariants_of(self, def_id.into_query_key())
387    }
388
389    pub fn constant_info(self, def_id: impl IntoQueryKey<DefId>) -> QueryResult<rty::ConstantInfo> {
390        self.inner
391            .queries
392            .constant_info(self, def_id.into_query_key())
393    }
394
395    /// The type of a static: its spec if it has one, or the default refinement of its rust type.
396    pub fn static_info(self, def_id: impl IntoQueryKey<DefId>) -> QueryResult<rty::Ty> {
397        self.inner
398            .queries
399            .static_info(self, def_id.into_query_key())
400    }
401
402    pub fn adt_sort_def_of(self, def_id: impl IntoQueryKey<DefId>) -> QueryResult<rty::AdtSortDef> {
403        self.inner
404            .queries
405            .adt_sort_def_of(self, def_id.into_query_key())
406    }
407
408    pub fn sort_decl_param_count(self, def_id: impl IntoQueryKey<FluxDefId>) -> usize {
409        self.inner
410            .queries
411            .sort_decl_param_count(self, def_id.into_query_key())
412    }
413
414    pub fn check_wf(self, def_id: LocalDefId) -> QueryResult<Rc<rty::WfckResults>> {
415        self.inner.queries.check_wf(self, def_id)
416    }
417
418    /// The refinement parameters of `def_id` that are *late bound*, i.e., bound in the fn signature
419    /// instead of being part of the item's refinement generics. This is the analog of rustc's
420    /// `is_late_bound_map` for refinement parameters.
421    ///
422    /// A refinement parameter of a fn-like item is late bound unless it is a location or it is
423    /// mentioned in a where-clause or in the bounds of an opaque type. Items that are not fn-like
424    /// have no late bound parameters.
425    pub fn late_bound_refinement_params(
426        self,
427        def_id: LocalDefId,
428    ) -> QueryResult<&'genv [fhir::ParamId]> {
429        self.inner
430            .queries
431            .late_bound_refinement_params(self, def_id)
432    }
433
434    pub fn impl_trait_ref(self, impl_id: DefId) -> QueryResult<rty::EarlyBinder<rty::TraitRef>> {
435        let trait_ref = self.tcx().impl_trait_ref(impl_id);
436        let trait_ref = trait_ref.skip_binder();
437        let trait_ref = trait_ref
438            .lower(self.tcx())
439            .map_err(|err| QueryErr::unsupported(impl_id, err.into_err()))?
440            .refine(&Refiner::default_for_item(self, impl_id)?)?;
441        Ok(rty::EarlyBinder(trait_ref))
442    }
443
444    pub fn generics_of(self, def_id: impl IntoQueryKey<DefId>) -> QueryResult<rty::Generics> {
445        self.inner
446            .queries
447            .generics_of(self, def_id.into_query_key())
448    }
449
450    pub fn refinement_generics_of(
451        self,
452        def_id: impl IntoQueryKey<DefId>,
453    ) -> QueryResult<rty::EarlyBinder<rty::RefinementGenerics>> {
454        self.inner
455            .queries
456            .refinement_generics_of(self, def_id.into_query_key())
457    }
458
459    pub fn predicates_of(
460        self,
461        def_id: impl IntoQueryKey<DefId>,
462    ) -> QueryResult<rty::EarlyBinder<rty::GenericPredicates>> {
463        self.inner
464            .queries
465            .predicates_of(self, def_id.into_query_key())
466    }
467
468    pub fn assoc_refinements_of(
469        self,
470        def_id: impl IntoQueryKey<DefId>,
471    ) -> QueryResult<rty::AssocRefinements> {
472        self.inner
473            .queries
474            .assoc_refinements_of(self, def_id.into_query_key())
475    }
476
477    pub fn assoc_refinement(self, assoc_id: FluxDefId) -> QueryResult<rty::AssocReft> {
478        Ok(self.assoc_refinements_of(assoc_id.parent())?.get(assoc_id))
479    }
480
481    /// Given the id of an associated refinement in a trait definition returns the body for the
482    /// corresponding associated refinement in the implementation with id `impl_id`.
483    ///
484    /// This function returns [`QueryErr::MissingAssocReft`] if the associated refinement is not
485    /// found in the implementation and there's no default body in the trait. This can happen if an
486    /// extern spec adds an associated refinement without a default body because we are currently
487    /// not checking `compare_impl_item` for those definitions.
488    pub fn assoc_refinement_body_for_impl(
489        self,
490        trait_assoc_id: FluxDefId,
491        impl_id: DefId,
492    ) -> QueryResult<rty::EarlyBinder<rty::Lambda>> {
493        // Check if the implementation has the associated refinement
494        let impl_assoc_refts = self.assoc_refinements_of(impl_id)?;
495        if let Some(impl_assoc_reft) = impl_assoc_refts.find(trait_assoc_id.name()) {
496            return self.assoc_refinement_body(impl_assoc_reft.def_id());
497        }
498
499        // Otherwise, check if the trait has a default body
500        if let Some(body) = self.default_assoc_refinement_body(trait_assoc_id)? {
501            let impl_trait_ref = self.impl_trait_ref(impl_id)?.instantiate_identity();
502            return Ok(rty::EarlyBinder(body.instantiate(self.tcx(), &impl_trait_ref.args, &[])));
503        }
504
505        Err(QueryErr::MissingAssocReft {
506            impl_id,
507            trait_id: trait_assoc_id.parent(),
508            name: trait_assoc_id.name(),
509        })
510    }
511
512    pub fn default_assoc_refinement_body(
513        self,
514        trait_assoc_id: FluxDefId,
515    ) -> QueryResult<Option<rty::EarlyBinder<rty::Lambda>>> {
516        self.inner
517            .queries
518            .default_assoc_refinement_body(self, trait_assoc_id)
519    }
520
521    pub fn assoc_refinement_body(
522        self,
523        impl_assoc_id: FluxDefId,
524    ) -> QueryResult<rty::EarlyBinder<rty::Lambda>> {
525        self.inner
526            .queries
527            .assoc_refinement_body(self, impl_assoc_id)
528    }
529
530    pub fn sort_of_assoc_reft(
531        self,
532        assoc_id: FluxDefId,
533    ) -> QueryResult<rty::EarlyBinder<rty::FuncSort>> {
534        self.inner.queries.sort_of_assoc_reft(self, assoc_id)
535    }
536
537    pub fn item_bounds(
538        self,
539        def_id: impl IntoQueryKey<DefId>,
540    ) -> QueryResult<rty::EarlyBinder<List<rty::Clause>>> {
541        self.inner
542            .queries
543            .item_bounds(self, def_id.into_query_key())
544    }
545
546    pub fn type_of(
547        self,
548        def_id: impl IntoQueryKey<DefId>,
549    ) -> QueryResult<rty::EarlyBinder<rty::TyOrCtor>> {
550        self.inner.queries.type_of(self, def_id.into_query_key())
551    }
552
553    pub fn fn_sig(
554        self,
555        def_id: impl IntoQueryKey<DefId>,
556    ) -> QueryResult<rty::EarlyBinder<rty::PolyFnSig>> {
557        self.inner.queries.fn_sig(self, def_id.into_query_key())
558    }
559
560    pub fn feed_weak_kvars(self, def_id: DefId, wk: WeakKvarMap) {
561        self.inner
562            .weak_kvars
563            .borrow_mut()
564            .insert(def_id, Rc::new(wk));
565    }
566
567    pub fn weak_kvars_for(self, def_id: DefId) -> Option<Rc<WeakKvarMap>> {
568        self.inner.weak_kvars.borrow().get(&def_id).cloned()
569    }
570
571    pub fn variants_of(
572        self,
573        def_id: impl IntoQueryKey<DefId>,
574    ) -> QueryResult<rty::Opaqueness<rty::EarlyBinder<rty::PolyVariants>>> {
575        self.inner
576            .queries
577            .variants_of(self, def_id.into_query_key())
578    }
579
580    pub fn variant_sig(
581        self,
582        def_id: DefId,
583        variant_idx: VariantIdx,
584    ) -> QueryResult<rty::Opaqueness<rty::EarlyBinder<rty::PolyVariant>>> {
585        Ok(self
586            .variants_of(def_id)?
587            .map(|variants| variants.map(|variants| variants[variant_idx.as_usize()].clone())))
588    }
589
590    /// Whether the crate has Flux metadata in the cratestore.
591    pub fn cstore_has_crate(self, krate: CrateNum) -> bool {
592        self.cstore().has_crate(krate)
593    }
594
595    /// Whether the function is marked with `#[flux::no_panic]`
596    pub fn no_panic(self, def_id: impl IntoQueryKey<DefId>) -> bool {
597        self.inner.queries.no_panic(self, def_id.into_query_key())
598    }
599
600    pub fn assume_parametric_params(self, def_id: impl IntoQueryKey<DefId>) -> UnordSet<u32> {
601        self.inner
602            .queries
603            .assume_parametric_params(self, def_id.into_query_key())
604    }
605
606    pub fn is_box(&self, res: fhir::Res) -> bool {
607        res.is_box(self.tcx())
608    }
609
610    pub fn def_id_to_param_index(&self, def_id: DefId) -> u32 {
611        let parent = self.tcx().parent(def_id);
612        let generics = self.tcx().generics_of(parent);
613        generics.param_def_id_to_index(self.tcx(), def_id).unwrap()
614    }
615
616    pub(crate) fn cstore(self) -> &'genv CrateStoreDyn<'tcx> {
617        &*self.inner.cstore
618    }
619
620    pub fn has_trusted_impl(&self, def_id: DefId) -> bool {
621        if let Some(did) = self
622            .resolve_id(def_id)
623            .as_maybe_extern()
624            .map(|id| id.local_id())
625        {
626            self.trusted_impl(did)
627        } else {
628            false
629        }
630    }
631
632    /// The `Output` associated type is defined in `FnOnce`, and `Fn`/`FnMut`
633    /// inherit it, so this should suffice to check if the `def_id`
634    /// corresponds to `LangItem::FnOnceOutput`.
635    pub fn is_fn_output(&self, def_id: DefId) -> bool {
636        let def_span = self.tcx().def_span(def_id);
637        self.tcx()
638            .require_lang_item(LangItem::FnOnceOutput, def_span)
639            == def_id
640    }
641
642    /// Returns whether `def_id` is the `call` method in the `Fn` trait,
643    /// the `call_mut` method in the `FnMut` trait,
644    /// or the `call_once` method in the `FnOnce` trait.
645    pub fn is_fn_call(&self, def_id: DefId) -> bool {
646        let methods_and_names = [
647            (LangItem::Fn, sym::call),
648            (LangItem::FnMut, sym::call_mut),
649            (LangItem::FnOnce, sym::call_once),
650        ];
651        let tcx = self.tcx();
652        let Some(assoc_item) = tcx.opt_associated_item(def_id) else { return false };
653        let Some(trait_id) = assoc_item.trait_container(tcx) else { return false };
654
655        methods_and_names.iter().any(|(lang_item, method_name)| {
656            assoc_item.name() == *method_name && tcx.is_lang_item(trait_id, *lang_item)
657        })
658    }
659
660    /// Iterator over all local def ids that are not an extern spec
661    pub fn iter_local_def_id(self) -> impl Iterator<Item = LocalDefId> + use<'tcx, 'genv> {
662        self.tcx().iter_local_def_id().filter(move |&local_def_id| {
663            self.maybe_extern_id(local_def_id).is_local() && !self.is_dummy(local_def_id)
664        })
665    }
666
667    pub fn iter_extern_def_id(self) -> impl Iterator<Item = DefId> + use<'tcx, 'genv> {
668        self.tcx()
669            .iter_local_def_id()
670            .filter_map(move |local_def_id| self.maybe_extern_id(local_def_id).as_extern())
671    }
672
673    pub fn maybe_extern_id(self, local_id: LocalDefId) -> MaybeExternId {
674        self.collect_specs()
675            .local_id_to_extern_id
676            .get(&local_id)
677            .map_or_else(
678                || MaybeExternId::Local(local_id),
679                |def_id| MaybeExternId::Extern(local_id, *def_id),
680            )
681    }
682
683    #[expect(clippy::disallowed_methods)]
684    pub fn resolve_id(self, def_id: DefId) -> ResolvedDefId {
685        let maybe_extern_spec = self
686            .collect_specs()
687            .extern_id_to_local_id
688            .get(&def_id)
689            .copied();
690        if let Some(local_id) = maybe_extern_spec {
691            ResolvedDefId::ExternSpec(local_id, def_id)
692        } else if let Some(local_id) = def_id.as_local() {
693            debug_assert!(
694                self.maybe_extern_id(local_id).is_local(),
695                "def id points to dummy local item `{def_id:?}`"
696            );
697            ResolvedDefId::Local(local_id)
698        } else {
699            ResolvedDefId::Extern(def_id)
700        }
701    }
702
703    pub fn infer_opts(self, def_id: LocalDefId) -> config::InferOpts {
704        let mut opts = config::PartialInferOpts::default();
705        self.traverse_parents(def_id, |did| {
706            if let Some(o) = self.fhir_attr_map(did).infer_opts() {
707                opts.merge(&o);
708            }
709            None::<!>
710        });
711        opts.into()
712    }
713
714    fn matches_file_path<F>(&self, def_id: MaybeExternId, matcher: F) -> bool
715    where
716        F: Fn(&Path) -> bool,
717    {
718        let def_id = def_id.local_id();
719        let tcx = self.tcx();
720        let span = tcx.def_span(def_id);
721        let sm = tcx.sess.source_map();
722        let FileName::Real(file_name) = sm.span_to_filename(span) else { return true };
723        let Some(mut file_path) = file_name.local_path() else { return true };
724
725        // If the path is absolute try to normalize it to be relative to the working_dir
726        if file_path.is_absolute() {
727            let Some(working_dir) = sm.working_dir().local_path() else { return true };
728            let Ok(p) = file_path.strip_prefix(working_dir) else { return true };
729            file_path = p;
730        }
731
732        matcher(file_path)
733    }
734
735    fn matches_def(&self, def_id: MaybeExternId, def: &str) -> bool {
736        // Does this def_id's name contain `fn_name`?
737        let def_path = self.tcx().def_path_str(def_id.local_id());
738        def_path.contains(def)
739    }
740
741    fn matches_pos(&self, def_id: MaybeExternId, line: usize, col: usize) -> bool {
742        let def_id = def_id.local_id();
743        let tcx = self.tcx();
744        let hir_id = tcx.local_def_id_to_hir_id(def_id);
745        let body_span = tcx.hir_span_with_body(hir_id);
746        let source_map = tcx.sess.source_map();
747        let lo_pos = source_map.lookup_char_pos(body_span.lo());
748        let start_line = lo_pos.line;
749        let start_col = lo_pos.col_display;
750        let hi_pos = source_map.lookup_char_pos(body_span.hi());
751        let end_line = hi_pos.line;
752        let end_col = hi_pos.col_display;
753
754        // is the line in the range of the body?
755        if start_line < end_line {
756            // multiple lines: check if the line is in the range
757            start_line <= line && line <= end_line
758        } else {
759            // single line: check if the line is the same and the column is in range
760            start_line == line && start_col <= col && col <= end_col
761        }
762    }
763
764    /// Check whether the `def_id` (or the file where `def_id` is defined)
765    /// is in the `include` pattern, and conservatively return `true` if
766    /// anything unexpected happens.
767    fn matches_pattern(&self, def_id: MaybeExternId, pattern: &IncludePattern) -> bool {
768        if self.matches_file_path(def_id, |path| pattern.glob.is_match(path)) {
769            return true;
770        }
771        if pattern.defs.iter().any(|def| self.matches_def(def_id, def)) {
772            return true;
773        }
774        if pattern.spans.iter().any(|pos| {
775            self.matches_file_path(def_id, |path| path.ends_with(&pos.file))
776                && self.matches_pos(def_id, pos.line, pos.column)
777        }) {
778            return true;
779        }
780        false
781    }
782
783    /// Check whether the `def_id` (or the file where `def_id` is defined)
784    /// is in the `include_trusted` pattern, and conservatively return `false` if
785    /// anything unexpected happens.
786    fn matches_trusted_pattern(&self, def_id: MaybeExternId) -> bool {
787        let Some(pattern) = config::trusted_pattern() else { return false };
788        self.matches_pattern(def_id, pattern)
789    }
790
791    /// Check whether the `def_id` (or the file where `def_id` is defined)
792    /// is in the `include_trusted_impl` pattern, and conservatively return `false` if
793    /// anything unexpected happens.
794    fn matches_trusted_impl_pattern(&self, def_id: MaybeExternId) -> bool {
795        let Some(pattern) = config::trusted_impl_pattern() else { return false };
796        self.matches_pattern(def_id, pattern)
797    }
798
799    /// Check whether the `def_id` (or the file where `def_id` is defined)
800    /// is in the `include` pattern, and conservatively return `true` if
801    /// anything unexpected happens.
802    fn matches_included_pattern(&self, def_id: MaybeExternId) -> bool {
803        let Some(pattern) = config::include_pattern() else { return true };
804        self.matches_pattern(def_id, pattern)
805    }
806
807    pub fn included(&self, def_id: MaybeExternId) -> bool {
808        self.matches_included_pattern(def_id) || self.matches_trusted_pattern(def_id)
809    }
810
811    /// Transitively follow the parent-chain of `def_id` to find the first containing item with an
812    /// explicit `#[flux::trusted(..)]` annotation and return whether that item is trusted or not.
813    /// If no explicit annotation is found, return `false`.
814    pub fn trusted(self, def_id: LocalDefId) -> bool {
815        let annotation = self
816            .traverse_parents(def_id, |did| self.fhir_attr_map(did).trusted())
817            .map(|trusted| trusted.to_bool())
818            .unwrap_or_else(config::trusted_default);
819        annotation || self.matches_trusted_pattern(MaybeExternId::Local(def_id))
820    }
821
822    pub fn trusted_impl(self, def_id: LocalDefId) -> bool {
823        let annotation = self
824            .traverse_parents(def_id, |did| self.fhir_attr_map(did).trusted_impl())
825            .map(|trusted| trusted.to_bool())
826            .unwrap_or(false);
827        annotation || self.matches_trusted_impl_pattern(MaybeExternId::Local(def_id))
828    }
829
830    /// Whether code generated by a `#[derive(..)]` on this ADT should be trusted.
831    /// A `#[flux::opaque]` type implicitly trusts its derives.
832    pub fn trusted_derive(self, def_id: LocalDefId) -> bool {
833        if let Some(trusted) = self.fhir_attr_map(def_id).trusted_derive() {
834            return trusted.to_bool();
835        }
836        self.adt_def(def_id)
837            .is_ok_and(|adt_def| adt_def.is_opaque())
838    }
839
840    /// For a `def_id` generated by a `#[derive(..)]`, the ADT the derive was applied to, i.e. the
841    /// self type of the enclosing impl.
842    ///
843    /// Returns `None` if `def_id` is not an impl item or if the self type is not a local ADT. A
844    /// derive is not obliged to generate impl items: a proc-macro derive can emit a free function
845    /// at module scope, or a function inside a generated trait. In those cases the parent is a
846    /// module or trait, for which `type_of` is not defined, so the parent's kind must be checked
847    /// before asking for its type.
848    pub fn derive_self_ty(self, def_id: LocalDefId) -> Option<LocalDefId> {
849        let tcx = self.tcx();
850        let impl_id = tcx.local_parent(def_id);
851        if !matches!(tcx.def_kind(impl_id), DefKind::Impl { .. }) {
852            return None;
853        }
854        let adt_def_id = tcx
855            .type_of(impl_id)
856            .instantiate_identity()
857            .skip_norm_wip()
858            .ty_adt_def()?
859            .did();
860        match self.resolve_id(adt_def_id) {
861            ResolvedDefId::Local(local_id) | ResolvedDefId::ExternSpec(local_id, _) => {
862                Some(local_id)
863            }
864            ResolvedDefId::Extern(_) => None,
865        }
866    }
867
868    /// Same behavior as [`trusted`], but for the `#[no_suggestions]` attribute.
869    /// (overridden to `true` by suggestions being globally disabled).
870    pub fn no_suggestions(self, def_id: LocalDefId) -> bool {
871        if !config::suggestions() {
872            return true;
873        }
874        self.traverse_parents(def_id, |did| {
875            // A parent has no_suggestions, we inherit it
876            if self.fhir_attr_map(did).no_suggestions() {
877                Some(true)
878            // It doesn't have it, keep trying
879            } else {
880                None
881            }
882        })
883        .unwrap_or(false)
884    }
885
886    /// Whether the item is a dummy item created by the extern spec macro.
887    ///
888    /// See [`crate::Specs::dummy_extern`]
889    pub fn is_dummy(self, def_id: LocalDefId) -> bool {
890        self.traverse_parents(def_id, |did| {
891            self.collect_specs()
892                .dummy_extern
893                .contains(&did)
894                .then_some(())
895        })
896        .is_some()
897    }
898
899    /// Transitively follow the parent-chain of `def_id` to find the first containing item with an
900    /// explicit `#[flux::ignore(..)]` annotation and return whether that item is ignored or not.
901    /// If no explicit annotation is found, return `false`.
902    pub fn ignored(self, def_id: LocalDefId) -> bool {
903        self.traverse_parents(def_id, |did| self.fhir_attr_map(did).ignored())
904            .map(|ignored| ignored.to_bool())
905            .unwrap_or_else(config::ignore_default)
906    }
907
908    /// Whether the function is marked with `#[flux::should_fail]`
909    pub fn should_fail(self, def_id: LocalDefId) -> bool {
910        self.fhir_attr_map(def_id).should_fail()
911    }
912
913    /// Whether the function is marked with `#[proven_externally]`
914    pub fn proven_externally(self, def_id: LocalDefId) -> Option<Span> {
915        self.fhir_attr_map(def_id).proven_externally()
916    }
917
918    /// Get the span of the #[sig(...)] attribute for a function, if it exists.
919    /// Checks local specs (including extern spec mapping) then falls back to cross-crate metadata.
920    pub fn spec_attr_span(self, def_id: DefId) -> Option<Span> {
921        let specs = self.collect_specs();
922        specs
923            .get_spec_attr_span(def_id)
924            .or_else(|| {
925                let local_id = specs.extern_id_to_local_id.get(&def_id)?;
926                specs.get_spec_attr_span(local_id.to_def_id())
927            })
928            .or_else(|| self.cstore().spec_attr_span(def_id))
929    }
930
931    /// Get the source text of the #[sig(...)] attribute for a function.
932    /// Reconstructs the string from the spec attribute span via the source map.
933    pub fn spec_attr_string(self, def_id: DefId) -> Option<String> {
934        let span = self.spec_attr_span(def_id)?;
935        self.tcx().sess.source_map().span_to_snippet(span).ok()
936    }
937
938    /// Traverse the parent chain of `def_id` until the first node for which `f` returns [`Some`].
939    fn traverse_parents<T>(
940        self,
941        mut def_id: LocalDefId,
942        mut f: impl FnMut(LocalDefId) -> Option<T>,
943    ) -> Option<T> {
944        loop {
945            if let Some(v) = f(def_id) {
946                break Some(v);
947            }
948
949            if let Some(parent) = self.tcx().opt_local_parent(def_id) {
950                def_id = parent;
951            } else {
952                break None;
953            }
954        }
955    }
956}
957
958impl<'genv, 'tcx> GlobalEnv<'genv, 'tcx> {
959    pub fn fhir_iter_flux_items(
960        self,
961    ) -> impl Iterator<Item = (FluxLocalDefId, fhir::FluxItem<'genv>)> {
962        self.fhir_crate()
963            .items
964            .iter()
965            .map(|(id, item)| (*id, *item))
966    }
967
968    pub fn fhir_sort_decl(&self, def_id: FluxLocalDefId) -> Option<&fhir::SortDecl> {
969        self.fhir_crate().items.get(&def_id).and_then(|item| {
970            if let fhir::FluxItem::SortDecl(sort_decl) = item { Some(*sort_decl) } else { None }
971        })
972    }
973
974    pub fn fhir_spec_func_body(
975        &self,
976        def_id: FluxLocalDefId,
977    ) -> Option<&'genv fhir::SpecFunc<'genv>> {
978        self.fhir_crate()
979            .items
980            .get(&def_id)
981            .and_then(|item| if let fhir::FluxItem::Func(defn) = item { Some(*defn) } else { None })
982    }
983
984    pub fn fhir_qualifiers(self) -> impl Iterator<Item = &'genv fhir::Qualifier<'genv>> {
985        self.fhir_crate().items.values().filter_map(|item| {
986            if let fhir::FluxItem::Qualifier(qual) = item { Some(*qual) } else { None }
987        })
988    }
989
990    pub fn fhir_primop_props(self) -> impl Iterator<Item = &'genv fhir::PrimOpProp<'genv>> {
991        self.fhir_crate().items.values().filter_map(|item| {
992            if let fhir::FluxItem::PrimOpProp(prop) = item { Some(*prop) } else { None }
993        })
994    }
995
996    pub fn fhir_axioms(self) -> impl Iterator<Item = &'genv fhir::Axiom<'genv>> {
997        self.fhir_crate().items.values().filter_map(|item| {
998            if let fhir::FluxItem::Axiom(axiom) = item { Some(*axiom) } else { None }
999        })
1000    }
1001
1002    pub fn fhir_get_generics(
1003        self,
1004        def_id: LocalDefId,
1005    ) -> QueryResult<Option<&'genv fhir::Generics<'genv>>> {
1006        // We don't have nodes for closures and coroutines
1007        if matches!(self.def_kind(def_id), DefKind::Closure) {
1008            Ok(None)
1009        } else {
1010            Ok(Some(self.fhir_expect_owner_node(def_id)?.generics()))
1011        }
1012    }
1013
1014    /// Splits the refinement parameters in the generics of `def_id` into early and late bound
1015    /// parameters (in that order) preserving declaration order.
1016    /// See [`GlobalEnv::late_bound_refinement_params`].
1017    pub fn fhir_split_refinement_params(
1018        self,
1019        def_id: LocalDefId,
1020    ) -> QueryResult<(Vec<fhir::RefineParam<'genv>>, Vec<fhir::RefineParam<'genv>>)> {
1021        let Some(generics) = self.fhir_get_generics(def_id)? else {
1022            return Ok((vec![], vec![]));
1023        };
1024        let late_bound = self.late_bound_refinement_params(def_id)?;
1025        Ok(generics
1026            .refinement_params
1027            .iter()
1028            .copied()
1029            .partition(|param| !late_bound.contains(&param.id)))
1030    }
1031
1032    pub fn fhir_expect_refinement_kind(
1033        self,
1034        def_id: LocalDefId,
1035    ) -> QueryResult<&'genv fhir::RefinementKind<'genv>> {
1036        let kind = match &self.fhir_expect_item(def_id)?.kind {
1037            fhir::ItemKind::Enum(enum_def) => &enum_def.refinement,
1038            fhir::ItemKind::Struct(struct_def) => &struct_def.refinement,
1039            _ => bug!("expected struct, enum or type alias"),
1040        };
1041        Ok(kind)
1042    }
1043
1044    pub fn fhir_expect_item(self, def_id: LocalDefId) -> QueryResult<&'genv fhir::Item<'genv>> {
1045        if let fhir::Node::Item(item) = self.fhir_node(def_id)? {
1046            Ok(item)
1047        } else {
1048            Err(query_bug!(def_id, "expected item: `{def_id:?}`"))
1049        }
1050    }
1051
1052    pub fn fhir_expect_owner_node(self, def_id: LocalDefId) -> QueryResult<fhir::OwnerNode<'genv>> {
1053        let Some(owner) = self.fhir_node(def_id)?.as_owner() else {
1054            return Err(query_bug!(def_id, "cannot find owner node"));
1055        };
1056        Ok(owner)
1057    }
1058
1059    pub fn fhir_node(self, def_id: LocalDefId) -> QueryResult<fhir::Node<'genv>> {
1060        self.desugar(def_id)
1061    }
1062}
1063
1064#[macro_export]
1065macro_rules! try_alloc_slice {
1066    ($genv:expr, $slice:expr, $map:expr $(,)?) => {{
1067        let slice = $slice;
1068        $crate::try_alloc_slice!($genv, cap: slice.len(), slice.into_iter().map($map))
1069    }};
1070    ($genv:expr, cap: $cap:expr, $it:expr $(,)?) => {{
1071        let mut err = None;
1072        let slice = $genv.alloc_slice_with_capacity($cap, $it.into_iter().collect_errors(&mut err));
1073        err.map_or(Ok(slice), Err)
1074    }};
1075}
1076
1077impl ErrorEmitter for GlobalEnv<'_, '_> {
1078    fn emit<'a>(&'a self, err: impl rustc_errors::Diagnostic<'a>) -> rustc_span::ErrorGuaranteed {
1079        self.sess().emit(err)
1080    }
1081}
1082
1083fn lean_parent_dir(tcx: TyCtxt) -> PathBuf {
1084    tcx.sess
1085        .source_map()
1086        .working_dir()
1087        .local_path()
1088        .unwrap()
1089        .join(config::lean_dir())
1090}