flux_middle/
global_env.rs

1use std::{alloc, ptr, rc::Rc, slice};
2
3use flux_arc_interner::List;
4use flux_common::{bug, result::ErrorEmitter};
5use flux_config as config;
6use flux_errors::FluxSession;
7use flux_rustc_bridge::{self, lowering::Lower, mir, ty};
8use rustc_data_structures::unord::UnordSet;
9use rustc_hir::{
10    def::DefKind,
11    def_id::{CrateNum, DefId, LocalDefId},
12};
13use rustc_middle::{
14    query::IntoQueryParam,
15    ty::{TyCtxt, Variance},
16};
17pub use rustc_span::{Symbol, symbol::Ident};
18
19use crate::{
20    cstore::CrateStoreDyn,
21    def_id::{FluxDefId, FluxLocalDefId, MaybeExternId, ResolvedDefId},
22    fhir::{self, VariantIdx},
23    queries::{Providers, Queries, QueryErr, QueryResult},
24    rty::{
25        self,
26        refining::{Refine as _, Refiner},
27    },
28};
29
30#[derive(Clone, Copy)]
31pub struct GlobalEnv<'genv, 'tcx> {
32    inner: &'genv GlobalEnvInner<'genv, 'tcx>,
33}
34
35struct GlobalEnvInner<'genv, 'tcx> {
36    tcx: TyCtxt<'tcx>,
37    sess: &'genv FluxSession,
38    arena: &'genv fhir::Arena,
39    cstore: Box<CrateStoreDyn>,
40    queries: Queries<'genv, 'tcx>,
41}
42
43impl<'tcx> GlobalEnv<'_, 'tcx> {
44    pub fn enter<'a, R>(
45        tcx: TyCtxt<'tcx>,
46        sess: &'a FluxSession,
47        cstore: Box<CrateStoreDyn>,
48        arena: &'a fhir::Arena,
49        providers: Providers,
50        f: impl for<'genv> FnOnce(GlobalEnv<'genv, 'tcx>) -> R,
51    ) -> R {
52        let inner = GlobalEnvInner { tcx, sess, cstore, arena, queries: Queries::new(providers) };
53        f(GlobalEnv { inner: &inner })
54    }
55}
56
57impl<'genv, 'tcx> GlobalEnv<'genv, 'tcx> {
58    pub fn tcx(self) -> TyCtxt<'tcx> {
59        self.inner.tcx
60    }
61
62    pub fn hir(&self) -> rustc_middle::hir::map::Map<'tcx> {
63        self.tcx().hir()
64    }
65
66    pub fn sess(self) -> &'genv FluxSession {
67        self.inner.sess
68    }
69
70    pub fn collect_specs(self) -> &'genv crate::Specs {
71        self.inner.queries.collect_specs(self)
72    }
73
74    pub fn resolve_crate(self) -> &'genv crate::ResolverOutput {
75        self.inner.queries.resolve_crate(self)
76    }
77
78    pub fn desugar(self, def_id: LocalDefId) -> QueryResult<fhir::Node<'genv>> {
79        self.inner.queries.desugar(self, def_id)
80    }
81
82    pub fn fhir_crate(self) -> &'genv fhir::FluxItems<'genv> {
83        self.inner.queries.fhir_crate(self)
84    }
85
86    pub fn map(self) -> Map<'genv, 'tcx> {
87        Map::new(self, self.fhir_crate())
88    }
89
90    pub fn alloc<T>(&self, val: T) -> &'genv T {
91        self.inner.arena.alloc(val)
92    }
93
94    pub fn alloc_slice<T: Copy>(self, slice: &[T]) -> &'genv [T] {
95        self.inner.arena.alloc_slice_copy(slice)
96    }
97
98    pub fn alloc_slice_fill_iter<T, I>(self, it: I) -> &'genv [T]
99    where
100        I: IntoIterator<Item = T>,
101        I::IntoIter: ExactSizeIterator,
102    {
103        self.inner.arena.alloc_slice_fill_iter(it)
104    }
105
106    pub fn def_kind(&self, def_id: impl IntoQueryParam<DefId>) -> DefKind {
107        self.tcx().def_kind(def_id.into_query_param())
108    }
109
110    /// Allocates space to store `cap` elements of type `T`.
111    ///
112    /// The elements are initialized using the supplied iterator. At most `cap` elements will be
113    /// retrieved from the iterator. If the iterator yields fewer than `cap` elements, the returned
114    /// slice will be of length less than the allocated capacity.
115    ///
116    /// ## Panics
117    ///
118    /// Panics if reserving space for the slice fails.
119    pub fn alloc_slice_with_capacity<T, I>(self, cap: usize, it: I) -> &'genv [T]
120    where
121        I: IntoIterator<Item = T>,
122    {
123        let layout = alloc::Layout::array::<T>(cap).unwrap_or_else(|_| bug!("out of memory"));
124        let dst = self.inner.arena.alloc_layout(layout).cast::<T>();
125        unsafe {
126            let mut len = 0;
127            for (i, v) in it.into_iter().take(cap).enumerate() {
128                len += 1;
129                ptr::write(dst.as_ptr().add(i), v);
130            }
131
132            slice::from_raw_parts(dst.as_ptr(), len)
133        }
134    }
135
136    pub fn normalized_info(self, did: FluxDefId) -> rty::NormalizeInfo {
137        self.normalized_defns(did.krate()).func_info(did).clone()
138    }
139
140    pub fn normalized_defns(self, krate: CrateNum) -> Rc<rty::NormalizedDefns> {
141        self.inner.queries.normalized_defns(self, krate)
142    }
143
144    pub fn qualifiers(self) -> QueryResult<&'genv [rty::Qualifier]> {
145        self.inner.queries.qualifiers(self)
146    }
147
148    /// Return all the qualifiers that apply to an item, including both global and local qualifiers.
149    pub fn qualifiers_for(
150        self,
151        did: LocalDefId,
152    ) -> QueryResult<impl Iterator<Item = &'genv rty::Qualifier>> {
153        let names: UnordSet<_> = self.map().fn_quals_for(did)?.iter().copied().collect();
154        Ok(self
155            .qualifiers()?
156            .iter()
157            .filter(move |qual| qual.global || names.contains(&qual.def_id)))
158    }
159
160    pub fn reveals_for(self, did: LocalDefId) -> QueryResult<impl Iterator<Item = FluxDefId>> {
161        let reveals = self.map().fn_reveals_for(did)?;
162        Ok(reveals.iter().copied())
163    }
164
165    pub fn func_sort(self, def_id: impl IntoQueryParam<FluxDefId>) -> rty::PolyFuncSort {
166        self.inner
167            .queries
168            .func_sort(self, def_id.into_query_param())
169    }
170
171    pub fn should_inline_fun(self, def_id: FluxDefId) -> bool {
172        let is_poly = self.func_sort(def_id).params().len() > 0;
173        is_poly || !flux_config::smt_define_fun()
174    }
175
176    pub fn variances_of(self, did: DefId) -> &'tcx [Variance] {
177        self.tcx().variances_of(did)
178    }
179
180    pub fn mir(self, def_id: LocalDefId) -> QueryResult<Rc<mir::Body<'tcx>>> {
181        self.inner.queries.mir(self, def_id)
182    }
183
184    pub fn lower_generics_of(self, def_id: impl IntoQueryParam<DefId>) -> ty::Generics<'tcx> {
185        self.inner
186            .queries
187            .lower_generics_of(self, def_id.into_query_param())
188    }
189
190    pub fn lower_predicates_of(
191        self,
192        def_id: impl IntoQueryParam<DefId>,
193    ) -> QueryResult<ty::GenericPredicates> {
194        self.inner
195            .queries
196            .lower_predicates_of(self, def_id.into_query_param())
197    }
198
199    pub fn lower_type_of(
200        self,
201        def_id: impl IntoQueryParam<DefId>,
202    ) -> QueryResult<ty::EarlyBinder<ty::Ty>> {
203        self.inner
204            .queries
205            .lower_type_of(self, def_id.into_query_param())
206    }
207
208    pub fn lower_fn_sig(
209        self,
210        def_id: impl Into<DefId>,
211    ) -> QueryResult<ty::EarlyBinder<ty::PolyFnSig>> {
212        self.inner.queries.lower_fn_sig(self, def_id.into())
213    }
214
215    pub fn adt_def(self, def_id: impl IntoQueryParam<DefId>) -> QueryResult<rty::AdtDef> {
216        self.inner.queries.adt_def(self, def_id.into_query_param())
217    }
218
219    pub fn constant_info(
220        self,
221        def_id: impl IntoQueryParam<DefId>,
222    ) -> QueryResult<rty::ConstantInfo> {
223        self.inner
224            .queries
225            .constant_info(self, def_id.into_query_param())
226    }
227
228    pub fn adt_sort_def_of(
229        self,
230        def_id: impl IntoQueryParam<DefId>,
231    ) -> QueryResult<rty::AdtSortDef> {
232        self.inner
233            .queries
234            .adt_sort_def_of(self, def_id.into_query_param())
235    }
236
237    pub fn check_wf(self, def_id: LocalDefId) -> QueryResult<Rc<rty::WfckResults>> {
238        self.inner.queries.check_wf(self, def_id)
239    }
240
241    pub fn impl_trait_ref(
242        self,
243        impl_id: DefId,
244    ) -> QueryResult<Option<rty::EarlyBinder<rty::TraitRef>>> {
245        let Some(trait_ref) = self.tcx().impl_trait_ref(impl_id) else { return Ok(None) };
246        let trait_ref = trait_ref.skip_binder();
247        let trait_ref = trait_ref
248            .lower(self.tcx())
249            .map_err(|err| QueryErr::unsupported(trait_ref.def_id, err.into_err()))?
250            .refine(&Refiner::default_for_item(self, impl_id)?)?;
251        Ok(Some(rty::EarlyBinder(trait_ref)))
252    }
253
254    pub fn generics_of(self, def_id: impl IntoQueryParam<DefId>) -> QueryResult<rty::Generics> {
255        self.inner
256            .queries
257            .generics_of(self, def_id.into_query_param())
258    }
259
260    pub fn refinement_generics_of(
261        self,
262        def_id: impl IntoQueryParam<DefId>,
263    ) -> QueryResult<rty::EarlyBinder<rty::RefinementGenerics>> {
264        self.inner
265            .queries
266            .refinement_generics_of(self, def_id.into_query_param())
267    }
268
269    pub fn predicates_of(
270        self,
271        def_id: impl IntoQueryParam<DefId>,
272    ) -> QueryResult<rty::EarlyBinder<rty::GenericPredicates>> {
273        self.inner
274            .queries
275            .predicates_of(self, def_id.into_query_param())
276    }
277
278    pub fn assoc_refinements_of(
279        self,
280        def_id: impl IntoQueryParam<DefId>,
281    ) -> QueryResult<rty::AssocRefinements> {
282        self.inner
283            .queries
284            .assoc_refinements_of(self, def_id.into_query_param())
285    }
286
287    /// Given the id of an associated refinement in a trait definition returns the body for the
288    /// corresponding associated refinement in the implementation with id `impl_id`.
289    ///
290    /// This function returns [`QueryErr::MissingAssocReft`] if the associated refinement is not
291    /// found in the implementation and there's no default body in the trait. This can happen if an
292    /// extern spec adds an associated refinement without a default body because we are currently
293    /// not checking `compare_impl_item` for those definitions.
294    pub fn assoc_refinement_body_for_impl(
295        self,
296        trait_assoc_id: FluxDefId,
297        impl_id: DefId,
298    ) -> QueryResult<rty::EarlyBinder<rty::Lambda>> {
299        // Check if the implementation has the associated refinement
300        let impl_assoc_refts = self.assoc_refinements_of(impl_id)?;
301        if let Some(impl_assoc_id) = impl_assoc_refts.find(trait_assoc_id.name()) {
302            return self.assoc_refinement_body(impl_assoc_id);
303        }
304
305        // Otherwise, check if the trait has a default body
306        if let Some(body) = self.default_assoc_refinement_body(trait_assoc_id)? {
307            let impl_trait_ref = self
308                .impl_trait_ref(impl_id)?
309                .unwrap()
310                .instantiate_identity();
311            return Ok(rty::EarlyBinder(body.instantiate(self.tcx(), &impl_trait_ref.args, &[])));
312        }
313
314        Err(QueryErr::MissingAssocReft {
315            impl_id,
316            trait_id: trait_assoc_id.parent(),
317            name: trait_assoc_id.name(),
318        })
319    }
320
321    pub fn default_assoc_refinement_body(
322        self,
323        trait_assoc_id: FluxDefId,
324    ) -> QueryResult<Option<rty::EarlyBinder<rty::Lambda>>> {
325        self.inner
326            .queries
327            .default_assoc_refinement_body(self, trait_assoc_id)
328    }
329
330    pub fn assoc_refinement_body(
331        self,
332        impl_assoc_id: FluxDefId,
333    ) -> QueryResult<rty::EarlyBinder<rty::Lambda>> {
334        self.inner
335            .queries
336            .assoc_refinement_body(self, impl_assoc_id)
337    }
338
339    pub fn sort_of_assoc_reft(
340        self,
341        assoc_id: FluxDefId,
342    ) -> QueryResult<rty::EarlyBinder<rty::FuncSort>> {
343        self.inner.queries.sort_of_assoc_reft(self, assoc_id)
344    }
345
346    pub fn item_bounds(self, def_id: DefId) -> QueryResult<rty::EarlyBinder<List<rty::Clause>>> {
347        self.inner.queries.item_bounds(self, def_id)
348    }
349
350    pub fn type_of(
351        self,
352        def_id: impl IntoQueryParam<DefId>,
353    ) -> QueryResult<rty::EarlyBinder<rty::TyOrCtor>> {
354        self.inner.queries.type_of(self, def_id.into_query_param())
355    }
356
357    pub fn fn_sig(
358        self,
359        def_id: impl IntoQueryParam<DefId>,
360    ) -> QueryResult<rty::EarlyBinder<rty::PolyFnSig>> {
361        self.inner.queries.fn_sig(self, def_id.into_query_param())
362    }
363
364    pub fn variants_of(
365        self,
366        def_id: impl IntoQueryParam<DefId>,
367    ) -> QueryResult<rty::Opaqueness<rty::EarlyBinder<rty::PolyVariants>>> {
368        self.inner
369            .queries
370            .variants_of(self, def_id.into_query_param())
371    }
372
373    pub fn variant_sig(
374        self,
375        def_id: DefId,
376        variant_idx: VariantIdx,
377    ) -> QueryResult<rty::Opaqueness<rty::EarlyBinder<rty::PolyVariant>>> {
378        Ok(self
379            .variants_of(def_id)?
380            .map(|variants| variants.map(|variants| variants[variant_idx.as_usize()].clone())))
381    }
382
383    pub fn lower_late_bound_vars(
384        self,
385        def_id: LocalDefId,
386    ) -> QueryResult<List<ty::BoundVariableKind>> {
387        self.inner.queries.lower_late_bound_vars(self, def_id)
388    }
389
390    pub fn is_box(&self, res: fhir::Res) -> bool {
391        res.is_box(self.tcx())
392    }
393
394    pub fn def_id_to_param_index(&self, def_id: DefId) -> u32 {
395        let parent = self.tcx().parent(def_id);
396        let generics = self.tcx().generics_of(parent);
397        generics.param_def_id_to_index(self.tcx(), def_id).unwrap()
398    }
399
400    pub(crate) fn cstore(self) -> &'genv CrateStoreDyn {
401        &*self.inner.cstore
402    }
403
404    pub fn has_trusted_impl(&self, def_id: DefId) -> bool {
405        if let Some(did) = self
406            .resolve_id(def_id)
407            .as_maybe_extern()
408            .map(|id| id.local_id())
409        {
410            self.trusted_impl(did)
411        } else {
412            false
413        }
414    }
415
416    /// The `Output` associated type is defined in `FnOnce`, and `Fn`/`FnMut`
417    /// inherit it, so this should suffice to check if the `def_id`
418    /// corresponds to `LangItem::FnOnceOutput`.
419    pub fn is_fn_output(&self, def_id: DefId) -> bool {
420        self.tcx()
421            .require_lang_item(rustc_hir::LangItem::FnOnceOutput, None)
422            == def_id
423    }
424
425    /// Iterator over all local def ids that are not a extern spec
426    pub fn iter_local_def_id(self) -> impl Iterator<Item = LocalDefId> + use<'tcx, 'genv> {
427        self.tcx().iter_local_def_id().filter(move |&local_def_id| {
428            self.maybe_extern_id(local_def_id).is_local() && !self.is_dummy(local_def_id)
429        })
430    }
431
432    pub fn iter_extern_def_id(self) -> impl Iterator<Item = DefId> + use<'tcx, 'genv> {
433        self.tcx()
434            .iter_local_def_id()
435            .filter_map(move |local_def_id| self.maybe_extern_id(local_def_id).as_extern())
436    }
437
438    pub fn maybe_extern_id(self, local_id: LocalDefId) -> MaybeExternId {
439        self.collect_specs()
440            .local_id_to_extern_id
441            .get(&local_id)
442            .map_or_else(
443                || MaybeExternId::Local(local_id),
444                |def_id| MaybeExternId::Extern(local_id, *def_id),
445            )
446    }
447
448    #[expect(clippy::disallowed_methods)]
449    pub fn resolve_id(self, def_id: DefId) -> ResolvedDefId {
450        let maybe_extern_spec = self
451            .collect_specs()
452            .extern_id_to_local_id
453            .get(&def_id)
454            .copied();
455        if let Some(local_id) = maybe_extern_spec {
456            ResolvedDefId::ExternSpec(local_id, def_id)
457        } else if let Some(local_id) = def_id.as_local() {
458            ResolvedDefId::Local(local_id)
459        } else {
460            ResolvedDefId::Extern(def_id)
461        }
462    }
463
464    pub fn infer_opts(self, def_id: LocalDefId) -> config::InferOpts {
465        let mut opts = config::PartialInferOpts::default();
466        let specs = self.collect_specs();
467        self.traverse_parents(def_id, |did| {
468            if let Some(o) = specs.infer_opts.get(&did) {
469                opts.merge(o);
470            }
471            None::<!>
472        });
473        opts.into()
474    }
475
476    /// Transitively follow the parent-chain of `def_id` to find the first containing item with an
477    /// explicit `#[flux::trusted(..)]` annotation and return whether that item is trusted or not.
478    /// If no explicit annotation is found, return `false`.
479    pub fn trusted(self, def_id: LocalDefId) -> bool {
480        self.traverse_parents(def_id, |did| self.collect_specs().trusted.get(&did))
481            .map(|trusted| trusted.to_bool())
482            .unwrap_or_else(config::trusted_default)
483    }
484
485    pub fn trusted_impl(self, def_id: LocalDefId) -> bool {
486        self.collect_specs()
487            .trusted_impl
488            .get(&def_id)
489            .is_some_and(|trusted| trusted.to_bool())
490    }
491
492    /// Whether the item is a dummy item created by the extern spec macro.
493    ///
494    /// See [`crate::Specs::dummy_extern`]
495    pub fn is_dummy(self, def_id: LocalDefId) -> bool {
496        self.traverse_parents(def_id, |did| {
497            self.collect_specs()
498                .dummy_extern
499                .contains(&did)
500                .then_some(())
501        })
502        .is_some()
503    }
504
505    /// Transitively follow the parent-chain of `def_id` to find the first containing item with an
506    /// explicit `#[flux::ignore(..)]` annotation and return whether that item is ignored or not.
507    /// If no explicit annotation is found, return `false`.
508    pub fn ignored(self, def_id: LocalDefId) -> bool {
509        self.traverse_parents(def_id, |did| self.collect_specs().ignores.get(&did))
510            .map(|ignored| ignored.to_bool())
511            .unwrap_or_else(config::ignore_default)
512    }
513
514    /// Whether the function is marked with `#[flux::should_fail]`
515    pub fn should_fail(self, def_id: LocalDefId) -> bool {
516        self.collect_specs().should_fail.contains(&def_id)
517    }
518
519    /// Traverse the parent chain of `def_id` until the first node for which `f` returns [`Some`].
520    fn traverse_parents<T>(
521        self,
522        mut def_id: LocalDefId,
523        mut f: impl FnMut(LocalDefId) -> Option<T>,
524    ) -> Option<T> {
525        loop {
526            if let Some(v) = f(def_id) {
527                break Some(v);
528            }
529
530            if let Some(parent) = self.tcx().opt_local_parent(def_id) {
531                def_id = parent;
532            } else {
533                break None;
534            }
535        }
536    }
537}
538
539#[derive(Clone, Copy)]
540pub struct Map<'genv, 'tcx> {
541    genv: GlobalEnv<'genv, 'tcx>,
542    fhir: &'genv fhir::FluxItems<'genv>,
543}
544
545impl<'genv, 'tcx> Map<'genv, 'tcx> {
546    fn new(genv: GlobalEnv<'genv, 'tcx>, fhir: &'genv fhir::FluxItems<'genv>) -> Self {
547        Self { genv, fhir }
548    }
549
550    pub fn get_generics(
551        self,
552        def_id: LocalDefId,
553    ) -> QueryResult<Option<&'genv fhir::Generics<'genv>>> {
554        // We don't have nodes for closures and coroutines
555        if matches!(self.genv.def_kind(def_id), DefKind::Closure) {
556            Ok(None)
557        } else {
558            Ok(Some(self.expect_owner_node(def_id)?.generics()))
559        }
560    }
561
562    pub fn refinement_kind(
563        self,
564        def_id: LocalDefId,
565    ) -> QueryResult<&'genv fhir::RefinementKind<'genv>> {
566        let kind = match &self.expect_item(def_id)?.kind {
567            fhir::ItemKind::Enum(enum_def) => &enum_def.refinement,
568            fhir::ItemKind::Struct(struct_def) => &struct_def.refinement,
569            _ => bug!("expected struct, enum or type alias"),
570        };
571        Ok(kind)
572    }
573
574    pub fn flux_items(self) -> impl Iterator<Item = (FluxLocalDefId, fhir::FluxItem<'genv>)> {
575        self.fhir.items.iter().map(|(id, item)| (*id, *item))
576    }
577
578    pub fn spec_func(&self, def_id: FluxLocalDefId) -> Option<&'genv fhir::SpecFunc<'genv>> {
579        self.fhir
580            .items
581            .get(&def_id)
582            .and_then(|item| if let fhir::FluxItem::Func(defn) = item { Some(*defn) } else { None })
583    }
584
585    pub fn qualifiers(self) -> impl Iterator<Item = &'genv fhir::Qualifier<'genv>> {
586        self.fhir.items.values().filter_map(|item| {
587            if let fhir::FluxItem::Qualifier(qual) = item { Some(*qual) } else { None }
588        })
589    }
590
591    pub fn fn_quals_for(self, def_id: LocalDefId) -> QueryResult<&'genv [FluxLocalDefId]> {
592        // This is called on adts when checking invariants
593        if let Some(fn_sig) = self.expect_owner_node(def_id)?.fn_sig() {
594            Ok(fn_sig.qualifiers)
595        } else {
596            Ok(&[])
597        }
598    }
599
600    fn fn_reveals_for(self, def_id: LocalDefId) -> QueryResult<&'genv [FluxDefId]> {
601        if let Some(fn_sig) = self.expect_owner_node(def_id)?.fn_sig() {
602            Ok(fn_sig.reveals)
603        } else {
604            Ok(&[])
605        }
606    }
607
608    pub fn expect_item(self, def_id: LocalDefId) -> QueryResult<&'genv fhir::Item<'genv>> {
609        if let fhir::Node::Item(item) = self.node(def_id)? {
610            Ok(item)
611        } else {
612            bug!("expected item: `{def_id:?}`")
613        }
614    }
615
616    pub fn node(self, def_id: LocalDefId) -> QueryResult<fhir::Node<'genv>> {
617        self.genv.desugar(def_id)
618    }
619
620    pub fn expect_owner_node(self, def_id: LocalDefId) -> QueryResult<fhir::OwnerNode<'genv>> {
621        Ok(self.node(def_id)?.as_owner().unwrap())
622    }
623}
624
625#[macro_export]
626macro_rules! try_alloc_slice {
627    ($genv:expr, $slice:expr, $map:expr $(,)?) => {{
628        let slice = $slice;
629        $crate::try_alloc_slice!($genv, cap: slice.len(), slice.into_iter().map($map))
630    }};
631    ($genv:expr, cap: $cap:expr, $it:expr $(,)?) => {{
632        let mut err = None;
633        let slice = $genv.alloc_slice_with_capacity($cap, $it.into_iter().collect_errors(&mut err));
634        err.map_or(Ok(slice), Err)
635    }};
636}
637
638impl ErrorEmitter for GlobalEnv<'_, '_> {
639    fn emit<'a>(&'a self, err: impl rustc_errors::Diagnostic<'a>) -> rustc_span::ErrorGuaranteed {
640        self.sess().emit(err)
641    }
642}