1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
use std::{alloc, ptr, rc::Rc, slice};

use flux_arc_interner::List;
use flux_common::{bug, result::ErrorEmitter};
use flux_config::CrateConfig;
use flux_errors::FluxSession;
use flux_rustc_bridge::{self, lowering::Lower, mir, ty};
use rustc_hash::FxHashSet;
use rustc_hir::{
    def::DefKind,
    def_id::{DefId, LocalDefId},
};
use rustc_middle::{
    query::IntoQueryParam,
    ty::{TyCtxt, Variance},
};
pub use rustc_span::{symbol::Ident, Symbol};

use crate::{
    cstore::CrateStoreDyn,
    fhir::{self, VariantIdx},
    queries::{Providers, Queries, QueryErr, QueryResult},
    rty::{self, normalize::SpecFuncDefns, refining::Refiner},
    MaybeExternId, ResolvedDefId,
};

#[derive(Clone, Copy)]
pub struct GlobalEnv<'genv, 'tcx> {
    inner: &'genv GlobalEnvInner<'genv, 'tcx>,
}

struct GlobalEnvInner<'genv, 'tcx> {
    tcx: TyCtxt<'tcx>,
    sess: &'genv FluxSession,
    arena: &'genv fhir::Arena,
    cstore: Box<CrateStoreDyn>,
    queries: Queries<'genv, 'tcx>,
}

impl<'tcx> GlobalEnv<'_, 'tcx> {
    pub fn enter<'a, R>(
        tcx: TyCtxt<'tcx>,
        sess: &'a FluxSession,
        cstore: Box<CrateStoreDyn>,
        arena: &'a fhir::Arena,
        providers: Providers,
        f: impl for<'genv> FnOnce(GlobalEnv<'genv, 'tcx>) -> R,
    ) -> R {
        let inner = GlobalEnvInner { tcx, sess, cstore, arena, queries: Queries::new(providers) };
        f(GlobalEnv { inner: &inner })
    }
}

impl<'genv, 'tcx> GlobalEnv<'genv, 'tcx> {
    pub fn tcx(self) -> TyCtxt<'tcx> {
        self.inner.tcx
    }

    pub fn hir(&self) -> rustc_middle::hir::map::Map<'tcx> {
        self.tcx().hir()
    }

    pub fn sess(self) -> &'genv FluxSession {
        self.inner.sess
    }

    pub fn collect_specs(self) -> &'genv crate::Specs {
        self.inner.queries.collect_specs(self)
    }

    pub fn resolve_crate(self) -> &'genv crate::ResolverOutput {
        self.inner.queries.resolve_crate(self)
    }

    pub fn desugar(self, def_id: LocalDefId) -> QueryResult<fhir::Node<'genv>> {
        self.inner.queries.desugar(self, def_id)
    }

    pub fn fhir_crate(self) -> &'genv fhir::FluxItems<'genv> {
        self.inner.queries.fhir_crate(self)
    }

    pub fn map(self) -> Map<'genv, 'tcx> {
        Map::new(self, self.fhir_crate())
    }

    pub fn alloc<T>(&self, val: T) -> &'genv T {
        self.inner.arena.alloc(val)
    }

    pub fn alloc_slice<T: Copy>(self, slice: &[T]) -> &'genv [T] {
        self.inner.arena.alloc_slice_copy(slice)
    }

    pub fn alloc_slice_fill_iter<T, I>(self, it: I) -> &'genv [T]
    where
        I: IntoIterator<Item = T>,
        I::IntoIter: ExactSizeIterator,
    {
        self.inner.arena.alloc_slice_fill_iter(it)
    }

    pub fn def_kind(&self, def_id: impl IntoQueryParam<DefId>) -> DefKind {
        self.tcx().def_kind(def_id.into_query_param())
    }

    /// Allocates space to store `cap` elements of type `T`.
    ///
    /// The elements are initialized using the supplied iterator. At most `cap` elements will be
    /// retrieved from the iterator. If the iterator yields fewer than `cap` elements, the returned
    /// slice will be of length less than the allocated capacity.
    ///
    /// ## Panics
    ///
    /// Panics if reserving space for the slice fails.
    pub fn alloc_slice_with_capacity<T, I>(self, cap: usize, it: I) -> &'genv [T]
    where
        I: IntoIterator<Item = T>,
    {
        let layout = alloc::Layout::array::<T>(cap).unwrap_or_else(|_| bug!("out of memory"));
        let dst = self.inner.arena.alloc_layout(layout).cast::<T>();
        unsafe {
            let mut len = 0;
            for (i, v) in it.into_iter().take(cap).enumerate() {
                len += 1;
                ptr::write(dst.as_ptr().add(i), v);
            }

            slice::from_raw_parts(dst.as_ptr(), len)
        }
    }

    pub fn spec_func_defns(&self) -> QueryResult<&SpecFuncDefns> {
        self.inner.queries.spec_func_defns(*self)
    }

    pub fn qualifiers(self) -> QueryResult<&'genv [rty::Qualifier]> {
        self.inner.queries.qualifiers(self)
    }

    /// Return all the qualifiers that apply to an item, including both global and local qualifiers.
    pub fn qualifiers_for(
        self,
        did: LocalDefId,
    ) -> QueryResult<impl Iterator<Item = &'genv rty::Qualifier>> {
        let names: FxHashSet<Symbol> = self
            .map()
            .fn_quals_for(did)?
            .iter()
            .map(|qual| qual.name)
            .collect();
        Ok(self
            .qualifiers()?
            .iter()
            .filter(move |qualifier| qualifier.global || names.contains(&qualifier.name)))
    }

    pub fn func_decl(self, name: Symbol) -> QueryResult<rty::SpecFuncDecl> {
        self.inner.queries.func_decl(self, name)
    }

    pub fn variances_of(self, did: DefId) -> &'tcx [Variance] {
        self.tcx().variances_of(did)
    }

    pub fn mir(self, def_id: LocalDefId) -> QueryResult<Rc<mir::Body<'tcx>>> {
        self.inner.queries.mir(self, def_id)
    }

    pub fn lower_generics_of(self, def_id: DefId) -> ty::Generics<'tcx> {
        self.inner.queries.lower_generics_of(self, def_id)
    }

    pub fn lower_predicates_of(
        self,
        def_id: impl Into<DefId>,
    ) -> QueryResult<ty::GenericPredicates> {
        self.inner.queries.lower_predicates_of(self, def_id.into())
    }

    pub fn lower_type_of(self, def_id: impl Into<DefId>) -> QueryResult<ty::EarlyBinder<ty::Ty>> {
        self.inner.queries.lower_type_of(self, def_id.into())
    }

    pub fn lower_fn_sig(
        self,
        def_id: impl Into<DefId>,
    ) -> QueryResult<ty::EarlyBinder<ty::PolyFnSig>> {
        self.inner.queries.lower_fn_sig(self, def_id.into())
    }

    pub fn adt_def(self, def_id: impl IntoQueryParam<DefId>) -> QueryResult<rty::AdtDef> {
        self.inner.queries.adt_def(self, def_id.into_query_param())
    }

    pub fn adt_sort_def_of(
        self,
        def_id: impl IntoQueryParam<DefId>,
    ) -> QueryResult<rty::AdtSortDef> {
        self.inner
            .queries
            .adt_sort_def_of(self, def_id.into_query_param())
    }

    pub fn check_wf(self, def_id: LocalDefId) -> QueryResult<Rc<rty::WfckResults>> {
        self.inner.queries.check_wf(self, def_id)
    }

    pub fn impl_trait_ref(
        self,
        impl_id: DefId,
    ) -> QueryResult<Option<rty::EarlyBinder<rty::TraitRef>>> {
        let Some(trait_ref) = self.tcx().impl_trait_ref(impl_id) else { return Ok(None) };
        let trait_ref = trait_ref.skip_binder();
        let trait_ref = trait_ref
            .lower(self.tcx())
            .map_err(|err| QueryErr::unsupported(trait_ref.def_id, err.into_err()))?;
        let trait_ref = Refiner::default(self, impl_id)?.refine_trait_ref(&trait_ref)?;
        Ok(Some(rty::EarlyBinder(trait_ref)))
    }

    pub fn generics_of(self, def_id: impl IntoQueryParam<DefId>) -> QueryResult<rty::Generics> {
        self.inner
            .queries
            .generics_of(self, def_id.into_query_param())
    }

    pub fn refinement_generics_of(
        self,
        def_id: impl IntoQueryParam<DefId>,
    ) -> QueryResult<rty::RefinementGenerics> {
        self.inner
            .queries
            .refinement_generics_of(self, def_id.into_query_param())
    }

    pub fn predicates_of(
        self,
        def_id: impl IntoQueryParam<DefId>,
    ) -> QueryResult<rty::EarlyBinder<rty::GenericPredicates>> {
        self.inner
            .queries
            .predicates_of(self, def_id.into_query_param())
    }

    pub fn assoc_refinements_of(
        self,
        def_id: impl IntoQueryParam<DefId>,
    ) -> QueryResult<rty::AssocRefinements> {
        self.inner
            .queries
            .assoc_refinements_of(self, def_id.into_query_param())
    }

    pub fn default_assoc_refinement_def(
        self,
        trait_id: DefId,
        name: Symbol,
    ) -> QueryResult<Option<rty::EarlyBinder<rty::Lambda>>> {
        self.inner
            .queries
            .default_assoc_refinement_def(self, trait_id, name)
    }

    pub fn assoc_refinement_def(
        self,
        impl_id: DefId,
        name: Symbol,
    ) -> QueryResult<rty::EarlyBinder<rty::Lambda>> {
        self.inner.queries.assoc_refinement_def(self, impl_id, name)
    }

    pub fn sort_of_assoc_reft(
        self,
        def_id: impl IntoQueryParam<DefId>,
        name: Symbol,
    ) -> QueryResult<Option<rty::EarlyBinder<rty::FuncSort>>> {
        self.inner
            .queries
            .sort_of_assoc_reft(self, def_id.into_query_param(), name)
    }

    pub fn item_bounds(self, def_id: DefId) -> QueryResult<rty::EarlyBinder<List<rty::Clause>>> {
        self.inner.queries.item_bounds(self, def_id)
    }

    pub fn type_of(
        self,
        def_id: impl IntoQueryParam<DefId>,
    ) -> QueryResult<rty::EarlyBinder<rty::TyOrCtor>> {
        self.inner.queries.type_of(self, def_id.into_query_param())
    }

    pub fn fn_sig(
        self,
        def_id: impl IntoQueryParam<DefId>,
    ) -> QueryResult<rty::EarlyBinder<rty::PolyFnSig>> {
        self.inner.queries.fn_sig(self, def_id.into_query_param())
    }

    pub fn variants_of(
        self,
        def_id: impl IntoQueryParam<DefId>,
    ) -> QueryResult<rty::Opaqueness<rty::EarlyBinder<rty::PolyVariants>>> {
        self.inner
            .queries
            .variants_of(self, def_id.into_query_param())
    }

    pub fn variant_sig(
        self,
        def_id: DefId,
        variant_idx: VariantIdx,
    ) -> QueryResult<rty::Opaqueness<rty::EarlyBinder<rty::PolyVariant>>> {
        Ok(self
            .variants_of(def_id)?
            .map(|variants| variants.map(|variants| variants[variant_idx.as_usize()].clone())))
    }

    pub fn lower_late_bound_vars(
        self,
        def_id: LocalDefId,
    ) -> QueryResult<List<ty::BoundVariableKind>> {
        self.inner.queries.lower_late_bound_vars(self, def_id)
    }

    pub fn is_box(&self, res: fhir::Res) -> bool {
        res.is_box(self.tcx())
    }

    pub fn def_id_to_param_index(&self, def_id: DefId) -> u32 {
        let parent = self.tcx().parent(def_id);
        let generics = self.tcx().generics_of(parent);
        generics.param_def_id_to_index(self.tcx(), def_id).unwrap()
    }

    pub(crate) fn cstore(self) -> &'genv CrateStoreDyn {
        &*self.inner.cstore
    }

    pub fn has_trusted_impl(&self, def_id: DefId) -> bool {
        if let Some(did) = self
            .resolve_id(def_id)
            .as_maybe_extern()
            .map(|id| id.local_id())
        {
            self.trusted_impl(did)
        } else {
            false
        }
    }

    pub fn is_fn_once_output(&self, def_id: DefId) -> bool {
        self.tcx()
            .require_lang_item(rustc_hir::LangItem::FnOnceOutput, None)
            == def_id
    }

    /// Iterator over all local def ids that are not a extern spec
    pub fn iter_local_def_id(self) -> impl Iterator<Item = LocalDefId> + use<'tcx, 'genv> {
        self.tcx().iter_local_def_id().filter(move |&local_def_id| {
            self.maybe_extern_id(local_def_id).is_local() && !self.is_dummy(local_def_id)
        })
    }

    pub fn iter_extern_def_id(self) -> impl Iterator<Item = DefId> + use<'tcx, 'genv> {
        self.tcx()
            .iter_local_def_id()
            .filter_map(move |local_def_id| self.maybe_extern_id(local_def_id).as_extern())
    }

    pub fn maybe_extern_id(self, local_id: LocalDefId) -> MaybeExternId {
        self.collect_specs()
            .local_id_to_extern_id
            .get(&local_id)
            .map_or_else(
                || MaybeExternId::Local(local_id),
                |def_id| MaybeExternId::Extern(local_id, *def_id),
            )
    }

    #[expect(clippy::disallowed_methods)]
    pub fn resolve_id(self, def_id: DefId) -> ResolvedDefId {
        let maybe_extern_spec = self
            .collect_specs()
            .extern_id_to_local_id
            .get(&def_id)
            .copied();
        if let Some(local_id) = maybe_extern_spec {
            ResolvedDefId::ExternSpec(local_id, def_id)
        } else if let Some(local_id) = def_id.as_local() {
            ResolvedDefId::Local(local_id)
        } else {
            ResolvedDefId::Extern(def_id)
        }
    }

    /// Transitively follow the parent-chain of `def_id` to find the first containing item with an
    /// explicit `#[flux::check_overflow(..)]` annotation and return whether that item has an
    /// explicitly annotation and whether it requires an overflow check or not.
    /// If no explicit annotation is found, return None
    ///
    /// Note:
    ///
    /// This uses Option since we want to be explicit about whether or not the
    /// check_overflow attribute was actually on an item
    ///
    /// This is relevant in cases where Flux is configured to check overflows globally
    /// but an item is marked `check_overflow(no)` explicitly.
    ///
    /// i.e. in these cases we need to know that
    /// 1. `check_overflow` was explicitly marked on an item
    /// 2. `check_overflow` boolean representation is false (`check_overflow(no)`)
    ///
    pub fn check_overflow(self, def_id: LocalDefId) -> Option<bool> {
        Some(
            self.traverse_parents(def_id, |did| self.collect_specs().check_overflows.get(&did))?
                .to_bool(),
        )
    }

    /// Transitively follow the parent-chain of `def_id` to find the first containing item with an
    /// explicit `#[flux::trusted(..)]` annotation and return whether that item is trusted or not.
    /// If no explicit annotation is found, return `false`.
    pub fn trusted(self, def_id: LocalDefId) -> bool {
        self.traverse_parents(def_id, |did| self.collect_specs().trusted.get(&did))
            .is_some_and(|trusted| trusted.to_bool())
    }

    pub fn trusted_impl(self, def_id: LocalDefId) -> bool {
        self.collect_specs()
            .trusted_impl
            .get(&def_id)
            .is_some_and(|trusted| trusted.to_bool())
    }

    /// Whether the item is a dummy item created by the extern spec macro.
    ///
    /// See [`crate::Specs::dummy_extern`]
    pub fn is_dummy(self, def_id: LocalDefId) -> bool {
        self.traverse_parents(def_id, |did| {
            self.collect_specs()
                .dummy_extern
                .contains(&did)
                .then_some(())
        })
        .is_some()
    }

    /// Transitively follow the parent-chain of `def_id` to find the first containing item with an
    /// explicit `#[flux::ignore(..)]` annotation and return whether that item is ignored or not.
    /// If no explicit annotation is found, return `false`.
    pub fn ignored(self, def_id: LocalDefId) -> bool {
        self.traverse_parents(def_id, |did| self.collect_specs().ignores.get(&did))
            .is_some_and(|ignored| ignored.to_bool())
    }

    /// Whether the function is marked with `#[flux::should_fail]`
    pub fn should_fail(self, def_id: LocalDefId) -> bool {
        self.collect_specs().should_fail.contains(&def_id)
    }

    /// Traverse the parent chain of `def_id` until the first node for which `f` returns [`Some`].
    fn traverse_parents<T>(
        self,
        mut def_id: LocalDefId,
        f: impl Fn(LocalDefId) -> Option<T>,
    ) -> Option<T> {
        loop {
            if let Some(v) = f(def_id) {
                break Some(v);
            }

            if let Some(parent) = self.tcx().opt_local_parent(def_id) {
                def_id = parent;
            } else {
                break None;
            }
        }
    }

    pub fn crate_config(self) -> Option<CrateConfig> {
        self.collect_specs().crate_config
    }
}

#[derive(Clone, Copy)]
pub struct Map<'genv, 'tcx> {
    genv: GlobalEnv<'genv, 'tcx>,
    fhir: &'genv fhir::FluxItems<'genv>,
}

impl<'genv, 'tcx> Map<'genv, 'tcx> {
    fn new(genv: GlobalEnv<'genv, 'tcx>, fhir: &'genv fhir::FluxItems<'genv>) -> Self {
        Self { genv, fhir }
    }

    pub fn get_generics(
        self,
        def_id: LocalDefId,
    ) -> QueryResult<Option<&'genv fhir::Generics<'genv>>> {
        // We don't have nodes for closures and coroutines
        if matches!(self.genv.def_kind(def_id), DefKind::Closure) {
            Ok(None)
        } else {
            Ok(Some(self.node(def_id)?.generics()))
        }
    }

    pub fn get_flux_item(self, name: Symbol) -> Option<&'genv fhir::FluxItem<'genv>> {
        self.fhir.items.get(&name).as_ref().copied()
    }

    pub fn refined_by(self, def_id: LocalDefId) -> QueryResult<&'genv fhir::RefinedBy<'genv>> {
        let refined_by = match &self.expect_item(def_id)?.kind {
            fhir::ItemKind::Enum(enum_def) => enum_def.refined_by,
            fhir::ItemKind::Struct(struct_def) => struct_def.refined_by,
            _ => bug!("expected struct, enum or type alias"),
        };
        Ok(refined_by)
    }

    pub fn spec_funcs(self) -> impl Iterator<Item = &'genv fhir::SpecFunc<'genv>> {
        self.fhir.items.values().filter_map(|item| {
            if let fhir::FluxItem::Func(defn) = item {
                Some(defn)
            } else {
                None
            }
        })
    }

    pub fn spec_func(&self, name: Symbol) -> Option<&'genv fhir::SpecFunc<'genv>> {
        self.fhir.items.get(&name).and_then(|item| {
            if let fhir::FluxItem::Func(defn) = item {
                Some(defn)
            } else {
                None
            }
        })
    }

    pub fn qualifiers(self) -> impl Iterator<Item = &'genv fhir::Qualifier<'genv>> {
        self.fhir.items.values().filter_map(|item| {
            if let fhir::FluxItem::Qualifier(qual) = item {
                Some(qual)
            } else {
                None
            }
        })
    }

    pub fn fn_quals_for(self, def_id: LocalDefId) -> QueryResult<&'genv [Ident]> {
        // This is called on adts when checking invariants
        if let Some(fn_sig) = self.node(def_id)?.fn_sig() {
            Ok(fn_sig.qualifiers)
        } else {
            Ok(&[])
        }
    }

    pub fn expect_item(self, def_id: LocalDefId) -> QueryResult<&'genv fhir::Item<'genv>> {
        if let fhir::Node::Item(item) = self.node(def_id)? {
            Ok(item)
        } else {
            bug!("expected item: `{def_id:?}`")
        }
    }

    pub fn node(self, def_id: LocalDefId) -> QueryResult<fhir::Node<'genv>> {
        self.genv.desugar(def_id)
    }
}

#[macro_export]
macro_rules! try_alloc_slice {
    ($genv:expr, $slice:expr, $map:expr $(,)?) => {{
        let slice = $slice;
        $crate::try_alloc_slice!($genv, cap: slice.len(), slice.into_iter().map($map))
    }};
    ($genv:expr, cap: $cap:expr, $it:expr $(,)?) => {{
        let mut err = None;
        let slice = $genv.alloc_slice_with_capacity($cap, $it.into_iter().collect_errors(&mut err));
        err.map_or(Ok(slice), Err)
    }};
}

impl ErrorEmitter for GlobalEnv<'_, '_> {
    fn emit<'a>(&'a self, err: impl rustc_errors::Diagnostic<'a>) -> rustc_span::ErrorGuaranteed {
        self.sess().emit(err)
    }
}