hax_frontend_exporter/
state.rs

1use crate::prelude::*;
2use paste::paste;
3
4macro_rules! mk_aux {
5    ($state:ident {$($lts:lifetime)*} $field:ident {$($field_type:tt)+} {$($gen:tt)*} {$($gen_full:tt)*} {$($params:tt)*} {$($fields:tt)*}) => {
6        paste ! {
7            pub trait [<Has $field:camel>]<$($lts,)*> {
8                fn $field(self: &Self) -> $($field_type)+<$($lts)*>;
9            }
10            impl<$($lts,)*$($gen)*> [<Has $field:camel>]<$($lts,)*> for $state<$($params)*> {
11                fn $field(self: &Self) -> $($field_type)+<$($lts)*> {
12                    self.$field.clone()
13                }
14            }
15        }
16    };
17}
18macro_rules! mk {
19    (struct $state:ident<$($glts:lifetime),*> {$($field:ident : {$($lts:lifetime),*} $field_type:ty),*$(,)?}) => {
20        mk!(@$state {} {$($field)*} {$($field: {$($lts),*} {$field_type},)*});
21    };
22    (@$state:ident {$($acc:tt)*} $fields:tt {
23        $field:ident : $lts:tt $field_type:tt
24        $(,$($rest:tt)*)?
25    }) => {mk!(@$state {
26        $($acc)* $fields $field: $lts $field_type,
27    } $fields {$($($rest)*)?} );};
28    (@$state:ident $body:tt $fields:tt {$(,)?}) => { mk! (@@ $state $body ); };
29    (@@$state:ident {$({$($fields:tt)*} $field:ident : {$($lts:lifetime)*} {$($field_type:tt)+},)*}) => {
30        paste! {
31            #[derive(Clone)]
32            pub struct $state<$([<$field:camel>],)*>{
33                $(pub $field: [<$field:camel>],)*
34            }
35        }
36        $(
37            macro_rules! __inner_helper {
38                ($gen:tt {$$($full_gen:tt)*} {$$($params:tt)*} $field $$($rest:tt)*) => {
39                    paste! {__inner_helper!(
40                        $gen {$$($full_gen)*[<$field:camel>],}
41                        {$$($params)*$($field_type)+<$($lts,)*>,} $$($rest)*
42                    );}
43                };
44                ({$$($gen:tt)*} {$$($full_gen:tt)*} {$$($params:tt)*} $i:ident $$($rest:tt)*) => {
45                    paste! {__inner_helper!(
46                        {$$($gen)*[<$i:camel>],} {$$($full_gen)*[<$i:camel>],}
47                        {$$($params)*[<$i:camel>],} $$($rest)*
48                    );}
49                };
50                ($gen:tt $full_gen:tt $params:tt $$(,)?) => {
51                    mk_aux!($state {$($lts)*} $field {$($field_type)+} $gen $full_gen $params {$($fields)*});
52                };
53            }
54            __inner_helper!({} {} {} $($fields)*);
55        )*
56    };
57}
58
59mod types {
60    use crate::prelude::*;
61    use rustc_middle::ty;
62    use std::{cell::RefCell, sync::Arc};
63
64    pub struct LocalContextS {
65        pub vars: HashMap<rustc_middle::thir::LocalVarId, String>,
66    }
67
68    impl Default for LocalContextS {
69        fn default() -> Self {
70            Self::new()
71        }
72    }
73
74    impl LocalContextS {
75        pub fn new() -> LocalContextS {
76            LocalContextS {
77                vars: HashMap::new(),
78            }
79        }
80    }
81
82    /// Global caches
83    #[derive(Default)]
84    pub struct GlobalCache<'tcx> {
85        /// Cache the `Span` translations.
86        pub spans: HashMap<rustc_span::Span, Span>,
87        /// Per-item cache.
88        pub per_item: HashMap<RDefId, ItemCache<'tcx>>,
89        /// A ID table session, providing fresh IDs.
90        pub id_table_session: id_table::Session,
91        /// Map that recovers rustc args for a given `ItemRef`.
92        pub reverse_item_refs_map: HashMap<id_table::Id, ty::GenericArgsRef<'tcx>>,
93        /// We create some artificial items; their def_ids are stored here. See the
94        /// `synthetic_items` module.
95        pub synthetic_def_ids: HashMap<SyntheticItem, RDefId>,
96        pub reverse_synthetic_map: HashMap<RDefId, SyntheticItem>,
97    }
98
99    /// Defines a mapping from types to types, for use with `TypeMap`.
100    pub struct FullDefMapper;
101    impl TypeMapper for FullDefMapper {
102        type Value<Body: TypeMappable> = Arc<FullDef<Body>>;
103    }
104
105    /// Per-item cache
106    #[derive(Default)]
107    pub struct ItemCache<'tcx> {
108        /// The translated `DefId`.
109        pub def_id: Option<DefId>,
110        /// The translated definitions, generic in the Body kind.
111        /// Each rustc `DefId` gives several hax `DefId`s: one for each promoted constant (if any),
112        /// and the base one represented by `None`. Moreover we can instantiate definitions with
113        /// generic arguments.
114        pub full_defs:
115            HashMap<(Option<PromotedId>, Option<ty::GenericArgsRef<'tcx>>), TypeMap<FullDefMapper>>,
116        /// Cache the `Ty` translations.
117        pub tys: HashMap<ty::Ty<'tcx>, Ty>,
118        /// Cache the `ItemRef` translations. This is fast because `GenericArgsRef` is interned.
119        pub item_refs: HashMap<(RDefId, ty::GenericArgsRef<'tcx>), ItemRef>,
120        /// Cache the trait resolution engine for each item.
121        pub predicate_searcher: Option<crate::traits::PredicateSearcher<'tcx>>,
122        /// Cache of trait refs to resolved impl expressions.
123        pub impl_exprs: HashMap<ty::PolyTraitRef<'tcx>, crate::traits::ImplExpr>,
124    }
125
126    #[derive(Clone)]
127    pub struct Base<'tcx> {
128        pub options: Rc<hax_frontend_exporter_options::Options>,
129        pub local_ctx: Rc<RefCell<LocalContextS>>,
130        pub opt_def_id: Option<rustc_hir::def_id::DefId>,
131        pub cache: Rc<RefCell<GlobalCache<'tcx>>>,
132        pub tcx: ty::TyCtxt<'tcx>,
133        /// Silence the warnings in case of trait resolution failure.
134        pub silence_resolution_errors: bool,
135    }
136
137    impl<'tcx> Base<'tcx> {
138        pub fn new(
139            tcx: rustc_middle::ty::TyCtxt<'tcx>,
140            options: hax_frontend_exporter_options::Options,
141        ) -> Self {
142            Self {
143                tcx,
144                cache: Default::default(),
145                options: Rc::new(options),
146                // Always prefer `s.owner_id()` to `s.base().opt_def_id`.
147                // `opt_def_id` is used in `utils` for error reporting
148                opt_def_id: None,
149                local_ctx: Rc::new(RefCell::new(LocalContextS::new())),
150                silence_resolution_errors: false,
151            }
152        }
153    }
154
155    pub type MacroCalls = Rc<HashMap<Span, Span>>;
156    pub type RcThir<'tcx> = Rc<rustc_middle::thir::Thir<'tcx>>;
157    pub type RcMir<'tcx> = Rc<rustc_middle::mir::Body<'tcx>>;
158    pub type UnitBinder<'tcx> = rustc_middle::ty::Binder<'tcx, ()>;
159}
160
161mk!(
162    struct State<'tcx> {
163        base: {'tcx} types::Base,
164        owner_id: {} rustc_hir::def_id::DefId,
165        thir: {'tcx} types::RcThir,
166        mir: {'tcx} types::RcMir,
167        binder: {'tcx} types::UnitBinder,
168        ty: {'tcx} rustc_middle::ty::Ty,
169    }
170);
171
172pub use self::types::*;
173
174pub type StateWithBase<'tcx> = State<Base<'tcx>, (), (), (), (), ()>;
175pub type StateWithOwner<'tcx> = State<Base<'tcx>, rustc_hir::def_id::DefId, (), (), (), ()>;
176pub type StateWithBinder<'tcx> =
177    State<Base<'tcx>, rustc_hir::def_id::DefId, (), (), types::UnitBinder<'tcx>, ()>;
178pub type StateWithThir<'tcx> =
179    State<Base<'tcx>, rustc_hir::def_id::DefId, types::RcThir<'tcx>, (), (), ()>;
180pub type StateWithThirAndTy<'tcx> = State<
181    Base<'tcx>,
182    rustc_hir::def_id::DefId,
183    types::RcThir<'tcx>,
184    (),
185    (),
186    rustc_middle::ty::Ty<'tcx>,
187>;
188pub type StateWithMir<'tcx> =
189    State<Base<'tcx>, rustc_hir::def_id::DefId, (), types::RcMir<'tcx>, (), ()>;
190
191impl<'tcx> StateWithBase<'tcx> {
192    pub fn new(
193        tcx: rustc_middle::ty::TyCtxt<'tcx>,
194        options: hax_frontend_exporter_options::Options,
195    ) -> Self {
196        Self {
197            base: Base::new(tcx, options),
198            owner_id: (),
199            thir: (),
200            mir: (),
201            binder: (),
202            ty: (),
203        }
204    }
205}
206
207pub trait BaseState<'tcx>: HasBase<'tcx> + Clone {
208    /// Updates the OnwerId in a state, making sure to override `opt_def_id` in base as well.
209    fn with_owner_id(&self, owner_id: rustc_hir::def_id::DefId) -> StateWithOwner<'tcx> {
210        let mut base = self.base();
211        base.opt_def_id = Some(owner_id);
212        State {
213            owner_id,
214            base,
215            thir: (),
216            mir: (),
217            binder: (),
218            ty: (),
219        }
220    }
221}
222impl<'tcx, T: HasBase<'tcx> + Clone> BaseState<'tcx> for T {}
223
224/// State of anything below an `owner`.
225pub trait UnderOwnerState<'tcx>: BaseState<'tcx> + HasOwnerId {
226    fn with_base(&self, base: types::Base<'tcx>) -> StateWithOwner<'tcx> {
227        State {
228            owner_id: self.owner_id(),
229            base,
230            thir: (),
231            mir: (),
232            binder: (),
233            ty: (),
234        }
235    }
236    fn with_binder(&self, binder: types::UnitBinder<'tcx>) -> StateWithBinder<'tcx> {
237        State {
238            base: self.base(),
239            owner_id: self.owner_id(),
240            binder,
241            thir: (),
242            mir: (),
243            ty: (),
244        }
245    }
246    fn with_thir(&self, thir: types::RcThir<'tcx>) -> StateWithThir<'tcx> {
247        State {
248            base: self.base(),
249            owner_id: self.owner_id(),
250            thir,
251            mir: (),
252            binder: (),
253            ty: (),
254        }
255    }
256    fn with_mir(&self, mir: types::RcMir<'tcx>) -> StateWithMir<'tcx> {
257        State {
258            base: self.base(),
259            owner_id: self.owner_id(),
260            mir,
261            thir: (),
262            binder: (),
263            ty: (),
264        }
265    }
266}
267impl<'tcx, T: BaseState<'tcx> + HasOwnerId> UnderOwnerState<'tcx> for T {}
268
269/// State of anything below a binder.
270pub trait UnderBinderState<'tcx> = UnderOwnerState<'tcx> + HasBinder<'tcx>;
271
272/// While translating expressions, we expect to always have a THIR
273/// body and an `owner_id` in the state
274pub trait ExprState<'tcx>: UnderOwnerState<'tcx> + HasThir<'tcx> {
275    fn with_ty(&self, ty: rustc_middle::ty::Ty<'tcx>) -> StateWithThirAndTy<'tcx> {
276        State {
277            base: self.base(),
278            owner_id: self.owner_id(),
279            thir: self.thir(),
280            mir: (),
281            binder: (),
282            ty,
283        }
284    }
285}
286impl<'tcx, T> ExprState<'tcx> for T where T: UnderOwnerState<'tcx> + HasThir<'tcx> {}
287
288pub trait WithGlobalCacheExt<'tcx>: BaseState<'tcx> {
289    /// Access the global cache. You must not call `sinto` within this function as this will likely
290    /// result in `BorrowMut` panics.
291    fn with_global_cache<T>(&self, f: impl FnOnce(&mut GlobalCache<'tcx>) -> T) -> T {
292        let base = self.base();
293        let mut cache = base.cache.borrow_mut();
294        f(&mut *cache)
295    }
296    /// Access the cache for a given item. You must not call `sinto` within this function as this
297    /// will likely result in `BorrowMut` panics.
298    fn with_item_cache<T>(&self, def_id: RDefId, f: impl FnOnce(&mut ItemCache<'tcx>) -> T) -> T {
299        self.with_global_cache(|cache| f(cache.per_item.entry(def_id).or_default()))
300    }
301}
302impl<'tcx, S: BaseState<'tcx>> WithGlobalCacheExt<'tcx> for S {}
303
304pub trait WithItemCacheExt<'tcx>: UnderOwnerState<'tcx> {
305    /// Access the cache for the current item. You must not call `sinto` within this function as
306    /// this will likely result in `BorrowMut` panics.
307    fn with_cache<T>(&self, f: impl FnOnce(&mut ItemCache<'tcx>) -> T) -> T {
308        self.with_item_cache(self.owner_id(), f)
309    }
310    fn with_predicate_searcher<T>(&self, f: impl FnOnce(&mut PredicateSearcher<'tcx>) -> T) -> T {
311        self.with_cache(|cache| {
312            f(cache.predicate_searcher.get_or_insert_with(|| {
313                PredicateSearcher::new_for_owner(
314                    self.base().tcx,
315                    self.owner_id(),
316                    self.base().options.bounds_options,
317                )
318            }))
319        })
320    }
321}
322impl<'tcx, S: UnderOwnerState<'tcx>> WithItemCacheExt<'tcx> for S {}
323
324impl ImplInfos {
325    fn from<'tcx, S: BaseState<'tcx>>(s: &S, did: rustc_hir::def_id::DefId) -> Self {
326        let tcx = s.base().tcx;
327        let s = &s.with_owner_id(did);
328
329        Self {
330            generics: tcx.generics_of(did).sinto(s),
331            typ: tcx.type_of(did).instantiate_identity().sinto(s),
332            trait_ref: match tcx.def_kind(did) {
333                rustc_hir::def::DefKind::Impl { of_trait: true } => {
334                    Some(tcx.impl_trait_ref(did).instantiate_identity().sinto(s))
335                }
336                _ => None,
337            },
338            clauses: predicates_defined_on(tcx, did).as_ref().sinto(s),
339        }
340    }
341}
342
343/// Returns a map from every implementation (`Impl`) `DefId`s to the
344/// type they implement, plus the bounds.
345pub fn impl_def_ids_to_impled_types_and_bounds<'tcx, S: BaseState<'tcx>>(
346    s: &S,
347) -> HashMap<DefId, ImplInfos> {
348    let tcx = s.base().tcx;
349
350    let def_ids: Vec<_> = s.with_global_cache(|cache| cache.per_item.keys().copied().collect());
351    let with_parents = |mut did: rustc_hir::def_id::DefId| {
352        let mut acc = vec![did];
353        while let Some(parent) = tcx.opt_parent(did) {
354            did = parent;
355            acc.push(did);
356        }
357        acc.into_iter()
358    };
359    use itertools::Itertools;
360    def_ids
361        .into_iter()
362        .flat_map(with_parents)
363        .unique()
364        .filter(|&did| {
365            // keep only DefIds that corresponds to implementations
366            matches!(
367                tcx.def_path(did).data.last(),
368                Some(rustc_hir::definitions::DisambiguatedDefPathData {
369                    data: rustc_hir::definitions::DefPathData::Impl,
370                    ..
371                })
372            )
373        })
374        .map(|did| (did.sinto(s), ImplInfos::from(s, did)))
375        .collect()
376}