hax_frontend_exporter/
rustc_utils.rs

1use crate::prelude::*;
2use rustc_hir::def::DefKind as RDefKind;
3use rustc_middle::{mir, ty};
4
5pub fn inst_binder<'tcx, T>(
6    tcx: ty::TyCtxt<'tcx>,
7    typing_env: ty::TypingEnv<'tcx>,
8    args: Option<ty::GenericArgsRef<'tcx>>,
9    x: ty::EarlyBinder<'tcx, T>,
10) -> T
11where
12    T: ty::TypeFoldable<ty::TyCtxt<'tcx>> + Clone,
13{
14    match args {
15        None => x.instantiate_identity(),
16        Some(args) => tcx.normalize_erasing_regions(typing_env, x.instantiate(tcx, args)),
17    }
18}
19
20pub fn substitute<'tcx, T>(
21    tcx: ty::TyCtxt<'tcx>,
22    typing_env: ty::TypingEnv<'tcx>,
23    args: Option<ty::GenericArgsRef<'tcx>>,
24    x: T,
25) -> T
26where
27    T: ty::TypeFoldable<ty::TyCtxt<'tcx>>,
28{
29    inst_binder(tcx, typing_env, args, ty::EarlyBinder::bind(x))
30}
31
32#[extension_traits::extension(pub trait SubstBinder)]
33impl<'tcx, T: ty::TypeFoldable<ty::TyCtxt<'tcx>>> ty::Binder<'tcx, T> {
34    fn subst(
35        self,
36        tcx: ty::TyCtxt<'tcx>,
37        generics: &[ty::GenericArg<'tcx>],
38    ) -> ty::Binder<'tcx, T> {
39        ty::EarlyBinder::bind(self).instantiate(tcx, generics)
40    }
41}
42
43/// Whether the item can have generic parameters.
44pub(crate) fn can_have_generics<'tcx>(tcx: ty::TyCtxt<'tcx>, def_id: RDefId) -> bool {
45    use RDefKind::*;
46    match get_def_kind(tcx, def_id) {
47        Mod | ConstParam | TyParam | LifetimeParam | Macro(..) | ExternCrate | Use | ForeignMod
48        | GlobalAsm => false,
49        _ => true,
50    }
51}
52
53#[tracing::instrument(skip(s))]
54pub(crate) fn get_variant_information<'s, S: UnderOwnerState<'s>>(
55    adt_def: &ty::AdtDef<'s>,
56    variant_index: rustc_abi::VariantIdx,
57    s: &S,
58) -> VariantInformations {
59    fn is_named<'s, I: std::iter::Iterator<Item = &'s ty::FieldDef> + Clone>(it: I) -> bool {
60        it.clone()
61            .any(|field| field.name.to_ident_string().parse::<u64>().is_err())
62    }
63    let variant_def = adt_def.variant(variant_index);
64    let variant = variant_def.def_id;
65    let constructs_type: DefId = adt_def.did().sinto(s);
66    let kind = if adt_def.is_struct() {
67        let named = is_named(adt_def.all_fields());
68        VariantKind::Struct { named }
69    } else if adt_def.is_union() {
70        VariantKind::Union
71    } else {
72        let named = is_named(variant_def.fields.iter());
73        let index = variant_index.into();
74        VariantKind::Enum { index, named }
75    };
76    VariantInformations {
77        typ: constructs_type.clone(),
78        variant: variant.sinto(s),
79        kind,
80        type_namespace: match &constructs_type.parent {
81            Some(parent) => parent.clone(),
82            None => {
83                let span = s.base().tcx.def_span(variant);
84                fatal!(
85                    s[span],
86                    "Type {:#?} appears to have no parent",
87                    constructs_type
88                )
89            }
90        },
91    }
92}
93
94#[tracing::instrument(skip(sess))]
95pub fn translate_span(span: rustc_span::Span, sess: &rustc_session::Session) -> Span {
96    let smap: &rustc_span::source_map::SourceMap = sess.psess.source_map();
97    let filename = smap.span_to_filename(span);
98
99    let lo = smap.lookup_char_pos(span.lo());
100    let hi = smap.lookup_char_pos(span.hi());
101
102    Span {
103        lo: lo.into(),
104        hi: hi.into(),
105        filename: filename.sinto(&()),
106        rust_span_data: Some(span.data()),
107    }
108}
109
110pub trait HasParamEnv<'tcx> {
111    fn param_env(&self) -> ty::ParamEnv<'tcx>;
112    fn typing_env(&self) -> ty::TypingEnv<'tcx>;
113}
114
115impl<'tcx, S: UnderOwnerState<'tcx>> HasParamEnv<'tcx> for S {
116    fn param_env(&self) -> ty::ParamEnv<'tcx> {
117        let tcx = self.base().tcx;
118        let def_id = self.owner_id();
119        if can_have_generics(tcx, def_id) {
120            tcx.param_env(def_id)
121        } else {
122            ty::ParamEnv::empty()
123        }
124    }
125    fn typing_env(&self) -> ty::TypingEnv<'tcx> {
126        ty::TypingEnv {
127            param_env: self.param_env(),
128            typing_mode: ty::TypingMode::PostAnalysis,
129        }
130    }
131}
132
133#[tracing::instrument(skip(s))]
134pub(crate) fn attribute_from_scope<'tcx, S: ExprState<'tcx>>(
135    s: &S,
136    scope: &rustc_middle::middle::region::Scope,
137) -> (Option<rustc_hir::hir_id::HirId>, Vec<Attribute>) {
138    let owner = s.owner_id();
139    let tcx = s.base().tcx;
140    let scope_tree = tcx.region_scope_tree(owner);
141    let hir_id = scope.hir_id(scope_tree);
142    let tcx = s.base().tcx;
143    let attributes = hir_id
144        .map(|hir_id| tcx.hir_attrs(hir_id).sinto(s))
145        .unwrap_or_default();
146    (hir_id, attributes)
147}
148
149/// Gets the closest ancestor of `id` that is the id of a type.
150pub fn get_closest_parent_type(
151    tcx: &ty::TyCtxt,
152    id: rustc_span::def_id::DefId,
153) -> rustc_span::def_id::DefId {
154    match tcx.def_kind(id) {
155        rustc_hir::def::DefKind::Union
156        | rustc_hir::def::DefKind::Struct
157        | rustc_hir::def::DefKind::Enum => id,
158        _ => get_closest_parent_type(tcx, tcx.parent(id)),
159    }
160}
161
162/// Gets the visibility (`pub` or not) of the definition. Returns `None` for defs that don't have a
163/// meaningful visibility.
164pub fn get_def_visibility<'tcx>(
165    tcx: ty::TyCtxt<'tcx>,
166    def_id: RDefId,
167    def_kind: RDefKind,
168) -> Option<bool> {
169    use RDefKind::*;
170    match def_kind {
171        AssocConst
172        | AssocFn
173        | Const
174        | Enum
175        | Field
176        | Fn
177        | ForeignTy
178        | Macro { .. }
179        | Mod
180        | Static { .. }
181        | Struct
182        | Trait
183        | TraitAlias
184        | TyAlias { .. }
185        | Union
186        | Use
187        | Variant => Some(tcx.visibility(def_id).is_public()),
188        // These kinds don't have visibility modifiers (which would cause `visibility` to panic).
189        AnonConst
190        | AssocTy
191        | Closure
192        | ConstParam
193        | Ctor { .. }
194        | ExternCrate
195        | ForeignMod
196        | GlobalAsm
197        | Impl { .. }
198        | InlineConst
199        | LifetimeParam
200        | OpaqueTy
201        | SyntheticCoroutineBody
202        | TyParam => None,
203    }
204}
205
206/// Gets the attributes of the definition.
207pub fn get_def_attrs<'tcx>(
208    tcx: ty::TyCtxt<'tcx>,
209    def_id: RDefId,
210    def_kind: RDefKind,
211) -> &'tcx [rustc_hir::Attribute] {
212    use RDefKind::*;
213    match def_kind {
214        // These kinds cause `get_attrs` to panic.
215        ConstParam | LifetimeParam | TyParam | ForeignMod => &[],
216        _ => {
217            if let Some(ldid) = def_id.as_local() {
218                tcx.hir_attrs(tcx.local_def_id_to_hir_id(ldid))
219            } else {
220                tcx.attrs_for_def(def_id)
221            }
222        }
223    }
224}
225
226/// Gets the children of a module.
227pub fn get_mod_children<'tcx>(
228    tcx: ty::TyCtxt<'tcx>,
229    def_id: RDefId,
230) -> Vec<(Option<rustc_span::Ident>, RDefId)> {
231    match def_id.as_local() {
232        Some(ldid) => match tcx.hir_node_by_def_id(ldid) {
233            rustc_hir::Node::Crate(m)
234            | rustc_hir::Node::Item(&rustc_hir::Item {
235                kind: rustc_hir::ItemKind::Mod(_, m),
236                ..
237            }) => m
238                .item_ids
239                .iter()
240                .map(|&item_id| {
241                    let opt_ident = tcx.hir_item(item_id).kind.ident();
242                    let def_id = item_id.owner_id.to_def_id();
243                    (opt_ident, def_id)
244                })
245                .collect(),
246            node => panic!("DefKind::Module is an unexpected node: {node:?}"),
247        },
248        None => tcx
249            .module_children(def_id)
250            .iter()
251            .map(|child| (Some(child.ident), child.res.def_id()))
252            .collect(),
253    }
254}
255
256/// Gets the children of an `extern` block. Empty if the block is not defined in the current crate.
257pub fn get_foreign_mod_children<'tcx>(tcx: ty::TyCtxt<'tcx>, def_id: RDefId) -> Vec<RDefId> {
258    match def_id.as_local() {
259        Some(ldid) => tcx
260            .hir_node_by_def_id(ldid)
261            .expect_item()
262            .expect_foreign_mod()
263            .1
264            .iter()
265            .map(|foreign_item_ref| foreign_item_ref.owner_id.to_def_id())
266            .collect(),
267        None => vec![],
268    }
269}
270
271/// The signature of a method impl may be a subtype of the one expected from the trait decl, as in
272/// the example below. For correctness, we must be able to map from the method generics declared in
273/// the trait to the actual method generics. Because this would require type inference, we instead
274/// simply return the declared signature. This will cause issues if it is possible to use such a
275/// more-specific implementation with its more-specific type, but we have a few other issues with
276/// lifetime-generic function pointers anyway so this is unlikely to cause problems.
277///
278/// ```ignore
279/// trait MyCompare<Other>: Sized {
280///     fn compare(self, other: Other) -> bool;
281/// }
282/// impl<'a> MyCompare<&'a ()> for &'a () {
283///     // This implementation is more general because it works for non-`'a` refs. Note that only
284///     // late-bound vars may differ in this way.
285///     // `<&'a () as MyCompare<&'a ()>>::compare` has type `fn<'b>(&'a (), &'b ()) -> bool`,
286///     // but type `fn(&'a (), &'a ()) -> bool` was expected from the trait declaration.
287///     fn compare<'b>(self, _other: &'b ()) -> bool {
288///         true
289///     }
290/// }
291/// ```
292pub fn get_method_sig<'tcx>(
293    tcx: ty::TyCtxt<'tcx>,
294    typing_env: ty::TypingEnv<'tcx>,
295    def_id: RDefId,
296    method_args: Option<ty::GenericArgsRef<'tcx>>,
297) -> ty::PolyFnSig<'tcx> {
298    let real_sig = inst_binder(tcx, typing_env, method_args, tcx.fn_sig(def_id));
299    let item = tcx.associated_item(def_id);
300    let ty::AssocContainer::TraitImpl(Ok(decl_method_id)) = item.container else {
301        return real_sig;
302    };
303    let declared_sig = tcx.fn_sig(decl_method_id);
304
305    // TODO(Nadrieril): Temporary hack: if the signatures have the same number of bound vars, we
306    // keep the real signature. While the declared signature is more correct, it is also less
307    // normalized and we can't normalize without erasing regions but regions are crucial in
308    // function signatures. Hence we cheat here, until charon gains proper normalization
309    // capabilities.
310    if declared_sig.skip_binder().bound_vars().len() == real_sig.bound_vars().len() {
311        return real_sig;
312    }
313
314    let impl_def_id = item.container_id(tcx);
315    let method_args =
316        method_args.unwrap_or_else(|| ty::GenericArgs::identity_for_item(tcx, def_id));
317    // The trait predicate that is implemented by the surrounding impl block.
318    let implemented_trait_ref = tcx
319        .impl_trait_ref(impl_def_id)
320        .instantiate(tcx, method_args);
321    // Construct arguments for the declared method generics in the context of the implemented
322    // method generics.
323    let decl_args = method_args.rebase_onto(tcx, impl_def_id, implemented_trait_ref.args);
324    let sig = declared_sig.instantiate(tcx, decl_args);
325    // Avoids accidentally using the same lifetime name twice in the same scope
326    // (once in impl parameters, second in the method declaration late-bound vars).
327    let sig = tcx.anonymize_bound_vars(sig);
328    normalize(tcx, typing_env, sig)
329}
330
331/// Generates a list of `<trait_ref>::Ty` type aliases for each non-gat associated type of the
332/// given trait and its parents, in a specific order.
333pub fn assoc_tys_for_trait<'tcx>(
334    tcx: ty::TyCtxt<'tcx>,
335    typing_env: ty::TypingEnv<'tcx>,
336    tref: ty::TraitRef<'tcx>,
337) -> Vec<ty::AliasTy<'tcx>> {
338    fn gather_assoc_tys<'tcx>(
339        tcx: ty::TyCtxt<'tcx>,
340        typing_env: ty::TypingEnv<'tcx>,
341        assoc_tys: &mut Vec<ty::AliasTy<'tcx>>,
342        tref: ty::TraitRef<'tcx>,
343    ) {
344        assoc_tys.extend(
345            tcx.associated_items(tref.def_id)
346                .in_definition_order()
347                .filter(|assoc| matches!(assoc.kind, ty::AssocKind::Type { .. }))
348                .filter(|assoc| tcx.generics_of(assoc.def_id).own_params.is_empty())
349                .map(|assoc| ty::AliasTy::new(tcx, assoc.def_id, tref.args)),
350        );
351        for clause in tcx
352            .explicit_super_predicates_of(tref.def_id)
353            .map_bound(|clauses| clauses.iter().map(|(clause, _span)| *clause))
354            .iter_instantiated(tcx, tref.args)
355        {
356            if let Some(pred) = clause.as_trait_clause() {
357                let tref = erase_and_norm(tcx, typing_env, pred.skip_binder().trait_ref);
358                gather_assoc_tys(tcx, typing_env, assoc_tys, tref);
359            }
360        }
361    }
362    let mut ret = vec![];
363    gather_assoc_tys(tcx, typing_env, &mut ret, tref);
364    ret
365}
366
367/// Generates a `dyn Trait<Args.., Ty = <Self as Trait>::Ty..>` type for the given trait ref.
368pub fn dyn_self_ty<'tcx>(
369    tcx: ty::TyCtxt<'tcx>,
370    typing_env: ty::TypingEnv<'tcx>,
371    tref: ty::TraitRef<'tcx>,
372) -> Option<ty::Ty<'tcx>> {
373    let re_erased = tcx.lifetimes.re_erased;
374    if !tcx.is_dyn_compatible(tref.def_id) {
375        return None;
376    }
377
378    // The main `Trait<Args>` predicate.
379    let main_pred = ty::Binder::dummy(ty::ExistentialPredicate::Trait(
380        ty::ExistentialTraitRef::erase_self_ty(tcx, tref),
381    ));
382
383    let ty_constraints = assoc_tys_for_trait(tcx, typing_env, tref)
384        .into_iter()
385        .map(|alias_ty| {
386            let proj = ty::ProjectionPredicate {
387                projection_term: alias_ty.into(),
388                term: ty::Ty::new_alias(tcx, ty::Projection, alias_ty).into(),
389            };
390            let proj = ty::ExistentialProjection::erase_self_ty(tcx, proj);
391            ty::Binder::dummy(ty::ExistentialPredicate::Projection(proj))
392        });
393
394    let preds = {
395        // Stable sort predicates to prevent platform-specific ordering issues
396        let mut preds: Vec<_> = [main_pred].into_iter().chain(ty_constraints).collect();
397        preds.sort_by(|a, b| {
398            use crate::rustc_middle::ty::ExistentialPredicateStableCmpExt;
399            a.skip_binder().stable_cmp(tcx, &b.skip_binder())
400        });
401        tcx.mk_poly_existential_predicates(&preds)
402    };
403    let ty = tcx.mk_ty_from_kind(ty::Dynamic(preds, re_erased));
404    let ty = normalize(tcx, typing_env, ty);
405    Some(ty)
406}
407
408pub fn closure_once_shim<'tcx>(
409    tcx: ty::TyCtxt<'tcx>,
410    closure_ty: ty::Ty<'tcx>,
411) -> Option<mir::Body<'tcx>> {
412    let ty::Closure(def_id, args) = closure_ty.kind() else {
413        unreachable!()
414    };
415    let instance = match args.as_closure().kind() {
416        ty::ClosureKind::Fn | ty::ClosureKind::FnMut => {
417            ty::Instance::fn_once_adapter_instance(tcx, *def_id, args)
418        }
419        ty::ClosureKind::FnOnce => return None,
420    };
421    let mir = tcx.instance_mir(instance.def).clone();
422    let mir = ty::EarlyBinder::bind(mir).instantiate(tcx, instance.args);
423    Some(mir)
424}
425
426pub fn drop_glue_shim<'tcx>(
427    tcx: ty::TyCtxt<'tcx>,
428    def_id: RDefId,
429    instantiate: Option<ty::GenericArgsRef<'tcx>>,
430) -> Option<mir::Body<'tcx>> {
431    let drop_in_place =
432        tcx.require_lang_item(rustc_hir::LangItem::DropInPlace, rustc_span::DUMMY_SP);
433    let ty = tcx.type_of(def_id);
434    let ty = match instantiate {
435        None => {
436            if !tcx.generics_of(def_id).is_empty() {
437                // Hack: layout code panics if it can't fully normalize types, which can happen e.g. with a
438                // trait associated type. For now we only translate the glue for monomorphic types.
439                return None;
440            }
441            ty.instantiate_identity()
442        }
443        Some(args) => ty.instantiate(tcx, args),
444    };
445    let instance_kind = ty::InstanceKind::DropGlue(drop_in_place, Some(ty));
446    let mir = tcx.instance_mir(instance_kind).clone();
447    Some(mir)
448}