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 ItemSource::TraitDecl { 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 ItemSource::TraitDecl { 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            span: trait_impl.item_meta.span,
50            source_text: fun_decl.item_meta.source_text,
51            attr_info: fun_decl.item_meta.attr_info,
52            lang_item: fun_decl.item_meta.lang_item,
53        };
54        fun_decl.src = ItemSource::TraitImpl {
55            impl_ref: TraitImplRef {
56                id: trait_impl.def_id,
57                generics: Box::new(trait_impl.generics.identity_args()),
58            },
59            trait_ref,
60            item_id,
61            reuses_default: true,
62        };
63        if !trait_impl.item_meta.opacity.is_transparent() {
64            fun_decl.body = Body::Opaque;
65        }
66
67        let generics = trait_impl
68            .generics
69            .identity_args_at_depth(DeBruijnId::one())
70            .concat(&method.params.identity_args_at_depth(DeBruijnId::zero()));
71        let generics = method.map_ref(|_| generics);
72
73        Some((fun_decl, generics))
74    }
75}
76
77impl TransformPass for Transform {
78    fn should_run(&self, options: &TranslateOptions) -> bool {
79        options.duplicate_defaulted_methods
80    }
81
82    fn transform_ctx(&self, ctx: &mut TransformCtx) {
83        let duplicated_methods: IndexMap<
84            TraitImplId,
85            Vec<(TraitMethodId, FunDecl, Binder<GenericArgs>)>,
86        > = ctx.translated.trait_impls.map_ref(|trait_impl| {
87            trait_impl
88                .methods
89                .iter_indexed()
90                .filter_map(|(method_id, method)| {
91                    let (fun_decl, generics) = Transform::prepare_defaulted_method_duplicate(
92                        ctx, trait_impl, method_id, method,
93                    )?;
94                    Some((method_id, fun_decl, generics))
95                })
96                .collect()
97        });
98
99        let mut methods_to_insert: Vec<(TraitMethodId, Binder<FunDeclRef>)> = Vec::new();
100        for (trait_impl_id, duplicates) in duplicated_methods.into_iter_indexed() {
101            if duplicates.is_empty() {
102                continue;
103            }
104            for (method_id, mut fun_decl, generics) in duplicates {
105                let new_id = ctx.translated.fun_decls.reserve_slot();
106                fun_decl.def_id = new_id;
107                // Takes care of adding to the names map as well.
108                ctx.translated
109                    .set_new_item_slot(ItemId::Fun(new_id), ItemByVal::Fun(fun_decl));
110
111                let method = generics.map(|generics| FunDeclRef {
112                    id: new_id,
113                    generics: Box::new(generics),
114                });
115                methods_to_insert.push((method_id, method));
116            }
117            let Some(trait_impl) = ctx.translated.trait_impls.get_mut(trait_impl_id) else {
118                continue;
119            };
120            for (method_id, method) in methods_to_insert.drain(..) {
121                trait_impl.methods.insert(method_id, method);
122            }
123        }
124    }
125}