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    if let Some(ldid) = def_id.as_local() {
213        tcx.hir_attrs(tcx.local_def_id_to_hir_id(ldid))
214    } else {
215        match def_kind {
216            // These kinds cause `get_attrs` to panic.
217            RDefKind::ConstParam | RDefKind::LifetimeParam | RDefKind::ForeignMod | RDefKind::TyParam => &[],
218            _ => tcx.attrs_for_def(def_id),
219        }
220    }
221}
222
223/// Gets the children of a module.
224pub fn get_mod_children<'tcx>(
225    tcx: ty::TyCtxt<'tcx>,
226    def_id: RDefId,
227) -> Vec<(Option<rustc_span::Ident>, RDefId)> {
228    match def_id.as_local() {
229        Some(ldid) => match tcx.hir_node_by_def_id(ldid) {
230            rustc_hir::Node::Crate(m)
231            | rustc_hir::Node::Item(&rustc_hir::Item {
232                kind: rustc_hir::ItemKind::Mod(_, m),
233                ..
234            }) => m
235                .item_ids
236                .iter()
237                .map(|&item_id| {
238                    let opt_ident = tcx.hir_item(item_id).kind.ident();
239                    let def_id = item_id.owner_id.to_def_id();
240                    (opt_ident, def_id)
241                })
242                .collect(),
243            node => panic!("DefKind::Module is an unexpected node: {node:?}"),
244        },
245        None => tcx
246            .module_children(def_id)
247            .iter()
248            .map(|child| (Some(child.ident), child.res.def_id()))
249            .collect(),
250    }
251}
252
253/// Gets the children of an `extern` block. Empty if the block is not defined in the current crate.
254pub fn get_foreign_mod_children<'tcx>(tcx: ty::TyCtxt<'tcx>, def_id: RDefId) -> Vec<RDefId> {
255    match def_id.as_local() {
256        Some(ldid) => tcx
257            .hir_node_by_def_id(ldid)
258            .expect_item()
259            .expect_foreign_mod()
260            .1
261            .iter()
262            .map(|foreign_item_ref| foreign_item_ref.owner_id.to_def_id())
263            .collect(),
264        None => vec![],
265    }
266}
267
268/// The signature of a method impl may be a subtype of the one expected from the trait decl, as in
269/// the example below. For correctness, we must be able to map from the method generics declared in
270/// the trait to the actual method generics. Because this would require type inference, we instead
271/// simply return the declared signature. This will cause issues if it is possible to use such a
272/// more-specific implementation with its more-specific type, but we have a few other issues with
273/// lifetime-generic function pointers anyway so this is unlikely to cause problems.
274///
275/// ```ignore
276/// trait MyCompare<Other>: Sized {
277///     fn compare(self, other: Other) -> bool;
278/// }
279/// impl<'a> MyCompare<&'a ()> for &'a () {
280///     // This implementation is more general because it works for non-`'a` refs. Note that only
281///     // late-bound vars may differ in this way.
282///     // `<&'a () as MyCompare<&'a ()>>::compare` has type `fn<'b>(&'a (), &'b ()) -> bool`,
283///     // but type `fn(&'a (), &'a ()) -> bool` was expected from the trait declaration.
284///     fn compare<'b>(self, _other: &'b ()) -> bool {
285///         true
286///     }
287/// }
288/// ```
289pub fn get_method_sig<'tcx>(
290    tcx: ty::TyCtxt<'tcx>,
291    typing_env: ty::TypingEnv<'tcx>,
292    def_id: RDefId,
293    method_args: Option<ty::GenericArgsRef<'tcx>>,
294) -> ty::PolyFnSig<'tcx> {
295    let real_sig = inst_binder(tcx, typing_env, method_args, tcx.fn_sig(def_id));
296    let item = tcx.associated_item(def_id);
297    let ty::AssocContainer::TraitImpl(Ok(decl_method_id)) = item.container else {
298        return real_sig;
299    };
300    let declared_sig = tcx.fn_sig(decl_method_id);
301
302    // TODO(Nadrieril): Temporary hack: if the signatures have the same number of bound vars, we
303    // keep the real signature. While the declared signature is more correct, it is also less
304    // normalized and we can't normalize without erasing regions but regions are crucial in
305    // function signatures. Hence we cheat here, until charon gains proper normalization
306    // capabilities.
307    if declared_sig.skip_binder().bound_vars().len() == real_sig.bound_vars().len() {
308        return real_sig;
309    }
310
311    let impl_def_id = item.container_id(tcx);
312    let method_args =
313        method_args.unwrap_or_else(|| ty::GenericArgs::identity_for_item(tcx, def_id));
314    // The trait predicate that is implemented by the surrounding impl block.
315    let implemented_trait_ref = tcx
316        .impl_trait_ref(impl_def_id)
317        .instantiate(tcx, method_args);
318    // Construct arguments for the declared method generics in the context of the implemented
319    // method generics.
320    let decl_args = method_args.rebase_onto(tcx, impl_def_id, implemented_trait_ref.args);
321    let sig = declared_sig.instantiate(tcx, decl_args);
322    // Avoids accidentally using the same lifetime name twice in the same scope
323    // (once in impl parameters, second in the method declaration late-bound vars).
324    let sig = tcx.anonymize_bound_vars(sig);
325    normalize(tcx, typing_env, sig)
326}
327
328/// Generates a list of `<trait_ref>::Ty` type aliases for each non-gat associated type of the
329/// given trait and its parents, in a specific order.
330pub fn assoc_tys_for_trait<'tcx>(
331    tcx: ty::TyCtxt<'tcx>,
332    typing_env: ty::TypingEnv<'tcx>,
333    tref: ty::TraitRef<'tcx>,
334) -> Vec<ty::AliasTy<'tcx>> {
335    fn gather_assoc_tys<'tcx>(
336        tcx: ty::TyCtxt<'tcx>,
337        typing_env: ty::TypingEnv<'tcx>,
338        assoc_tys: &mut Vec<ty::AliasTy<'tcx>>,
339        tref: ty::TraitRef<'tcx>,
340    ) {
341        assoc_tys.extend(
342            tcx.associated_items(tref.def_id)
343                .in_definition_order()
344                .filter(|assoc| matches!(assoc.kind, ty::AssocKind::Type { .. }))
345                .filter(|assoc| tcx.generics_of(assoc.def_id).own_params.is_empty())
346                .map(|assoc| ty::AliasTy::new(tcx, assoc.def_id, tref.args)),
347        );
348        for clause in tcx
349            .explicit_super_predicates_of(tref.def_id)
350            .map_bound(|clauses| clauses.iter().map(|(clause, _span)| *clause))
351            .iter_instantiated(tcx, tref.args)
352        {
353            if let Some(pred) = clause.as_trait_clause() {
354                let tref = erase_and_norm(tcx, typing_env, pred.skip_binder().trait_ref);
355                gather_assoc_tys(tcx, typing_env, assoc_tys, tref);
356            }
357        }
358    }
359    let mut ret = vec![];
360    gather_assoc_tys(tcx, typing_env, &mut ret, tref);
361    ret
362}
363
364/// Generates a `dyn Trait<Args.., Ty = <Self as Trait>::Ty..>` type for the given trait ref.
365pub fn dyn_self_ty<'tcx>(
366    tcx: ty::TyCtxt<'tcx>,
367    typing_env: ty::TypingEnv<'tcx>,
368    tref: ty::TraitRef<'tcx>,
369) -> Option<ty::Ty<'tcx>> {
370    let re_erased = tcx.lifetimes.re_erased;
371    if !tcx.is_dyn_compatible(tref.def_id) {
372        return None;
373    }
374
375    // The main `Trait<Args>` predicate.
376    let main_pred = ty::Binder::dummy(ty::ExistentialPredicate::Trait(
377        ty::ExistentialTraitRef::erase_self_ty(tcx, tref),
378    ));
379
380    let ty_constraints = assoc_tys_for_trait(tcx, typing_env, tref)
381        .into_iter()
382        .map(|alias_ty| {
383            let proj = ty::ProjectionPredicate {
384                projection_term: alias_ty.into(),
385                term: ty::Ty::new_alias(tcx, ty::Projection, alias_ty).into(),
386            };
387            let proj = ty::ExistentialProjection::erase_self_ty(tcx, proj);
388            ty::Binder::dummy(ty::ExistentialPredicate::Projection(proj))
389        });
390
391    let preds = {
392        // Stable sort predicates to prevent platform-specific ordering issues
393        let mut preds: Vec<_> = [main_pred].into_iter().chain(ty_constraints).collect();
394        preds.sort_by(|a, b| {
395            use crate::rustc_middle::ty::ExistentialPredicateStableCmpExt;
396            a.skip_binder().stable_cmp(tcx, &b.skip_binder())
397        });
398        tcx.mk_poly_existential_predicates(&preds)
399    };
400    let ty = tcx.mk_ty_from_kind(ty::Dynamic(preds, re_erased));
401    let ty = normalize(tcx, typing_env, ty);
402    Some(ty)
403}
404
405pub fn closure_once_shim<'tcx>(
406    tcx: ty::TyCtxt<'tcx>,
407    closure_ty: ty::Ty<'tcx>,
408) -> Option<mir::Body<'tcx>> {
409    let ty::Closure(def_id, args) = closure_ty.kind() else {
410        unreachable!()
411    };
412    let instance = match args.as_closure().kind() {
413        ty::ClosureKind::Fn | ty::ClosureKind::FnMut => {
414            ty::Instance::fn_once_adapter_instance(tcx, *def_id, args)
415        }
416        ty::ClosureKind::FnOnce => return None,
417    };
418    let mir = tcx.instance_mir(instance.def).clone();
419    let mir = ty::EarlyBinder::bind(mir).instantiate(tcx, instance.args);
420    Some(mir)
421}
422
423pub fn drop_glue_shim<'tcx>(
424    tcx: ty::TyCtxt<'tcx>,
425    def_id: RDefId,
426    instantiate: Option<ty::GenericArgsRef<'tcx>>,
427) -> Option<mir::Body<'tcx>> {
428    let drop_in_place =
429        tcx.require_lang_item(rustc_hir::LangItem::DropInPlace, rustc_span::DUMMY_SP);
430    let ty = tcx.type_of(def_id);
431    let ty = match instantiate {
432        None => {
433            if !tcx.generics_of(def_id).is_empty() {
434                // Hack: layout code panics if it can't fully normalize types, which can happen e.g. with a
435                // trait associated type. For now we only translate the glue for monomorphic types.
436                return None;
437            }
438            ty.instantiate_identity()
439        }
440        Some(args) => ty.instantiate(tcx, args),
441    };
442    let instance_kind = ty::InstanceKind::DropGlue(drop_in_place, Some(ty));
443    let mir = tcx.instance_mir(instance_kind).clone();
444    Some(mir)
445}