Skip to main content

charon_lib/transform/simplify_output/
duplicate_defaulted_methods.rs

1//! `--duplicate-defaulted-methods`: copy trait default methods into impls that use them.
2use crate::ast::*;
3use crate::options::TranslateOptions;
4use crate::transform::{TransformCtx, ctx::TransformPass};
5
6pub struct Transform;
7
8impl Transform {
9    fn prepare_defaulted_method_duplicate(
10        ctx: &TransformCtx,
11        trait_impl: &TraitImpl,
12        method_id: TraitMethodId,
13        method: &Binder<FunDeclRef>,
14    ) -> Option<(FunDecl, Binder<GenericArgs>)> {
15        let original_method_id = method.skip_binder.id;
16        let original_method = ctx.translated.fun_decls.get(original_method_id)?;
17        let FunSource::TraitDefault { item_id, .. } = &original_method.src else {
18            return None;
19        };
20        let item_id = *item_id;
21        let original_method = original_method.clone();
22        let method_name = ctx
23            .translated
24            .assoc_item_name(trait_impl.impl_trait.id, method_id);
25        let mut name = ctx.translated.item_name(trait_impl.def_id).clone();
26        name.name.push(PathElem::Ident(
27            method_name.to_string(),
28            Disambiguator::ZERO,
29        ));
30
31        // Flatten the impl binder and the method binder. The resulting substitution maps the
32        // trait default method into the generic context of the duplicated impl method.
33        let subst: Binder<FunDeclRef> = Binder {
34            params: trait_impl.generics.clone(),
35            skip_binder: method.clone(),
36            kind: BinderKind::Other,
37        }
38        .flatten();
39        let mut fun_decl = original_method.substitute_params(subst.map(|x| *x.generics));
40
41        let FunSource::TraitDefault { trait_ref, .. } = fun_decl.src else {
42            unreachable!()
43        };
44        fun_decl.def_id = FunDeclId::MAX; // We fix it up on insertion
45        fun_decl.item_meta = ItemMeta {
46            name,
47            opacity: trait_impl.item_meta.opacity,
48            is_local: trait_impl.item_meta.is_local,
49            started_from: false,
50            is_extern: fun_decl.item_meta.is_extern,
51            span: trait_impl.item_meta.span,
52            source_text: fun_decl.item_meta.source_text,
53            attr_info: fun_decl.item_meta.attr_info,
54            lang_item: fun_decl.item_meta.lang_item,
55            diagnostic_item: fun_decl.item_meta.diagnostic_item,
56            has_errors: fun_decl.item_meta.has_errors,
57        };
58        fun_decl.src = FunSource::TraitImpl {
59            impl_ref: TraitImplRef {
60                id: trait_impl.def_id,
61                generics: Box::new(trait_impl.generics.identity_args()),
62            },
63            trait_ref,
64            item_id,
65            reuses_default: true,
66        };
67        if !trait_impl.item_meta.opacity.is_transparent() {
68            fun_decl.body = Body::Opaque;
69        }
70
71        let generics = trait_impl
72            .generics
73            .identity_args_at_depth(DeBruijnId::one())
74            .concat(&method.params.identity_args_at_depth(DeBruijnId::zero()));
75        let generics = method.map_ref(|_| generics);
76
77        Some((fun_decl, generics))
78    }
79}
80
81impl TransformPass for Transform {
82    fn should_run(&self, options: &TranslateOptions) -> bool {
83        options.duplicate_defaulted_methods
84    }
85
86    fn transform_ctx(&self, ctx: &mut TransformCtx) {
87        let duplicated_methods: IndexMap<
88            TraitImplId,
89            Vec<(TraitMethodId, FunDecl, Binder<GenericArgs>)>,
90        > = ctx.translated.trait_impls.map_ref(|trait_impl| {
91            trait_impl
92                .methods
93                .iter_indexed()
94                .filter_map(|(method_id, method)| {
95                    let (fun_decl, generics) = Transform::prepare_defaulted_method_duplicate(
96                        ctx, trait_impl, method_id, method,
97                    )?;
98                    Some((method_id, fun_decl, generics))
99                })
100                .collect()
101        });
102
103        let mut methods_to_insert: Vec<(TraitMethodId, Binder<FunDeclRef>)> = Vec::new();
104        for (trait_impl_id, duplicates) in duplicated_methods.into_iter_indexed() {
105            if duplicates.is_empty() {
106                continue;
107            }
108            for (method_id, mut fun_decl, generics) in duplicates {
109                let new_id = ctx.translated.fun_decls.reserve_slot();
110                fun_decl.def_id = new_id;
111                // Takes care of adding to the names map as well.
112                ctx.translated
113                    .set_new_item_slot(ItemId::Fun(new_id), ItemByVal::Fun(fun_decl));
114
115                let method = generics.map(|generics| FunDeclRef {
116                    id: new_id,
117                    generics: Box::new(generics),
118                });
119                methods_to_insert.push((method_id, method));
120            }
121            let Some(trait_impl) = ctx.translated.trait_impls.get_mut(trait_impl_id) else {
122                continue;
123            };
124            for (method_id, method) in methods_to_insert.drain(..) {
125                trait_impl.methods.insert(method_id, method);
126            }
127        }
128    }
129}