charon_lib/transform/
duplicate_defaulted_methods.rs

1//! Add missing methods to trait impls by duplicating the default method.
2use std::{collections::HashMap, mem};
3
4use crate::ast::*;
5
6use super::{TransformCtx, ctx::TransformPass};
7
8pub struct Transform;
9impl TransformPass for Transform {
10    fn transform_ctx(&self, ctx: &mut TransformCtx) {
11        for impl_id in ctx.translated.trait_impls.all_indices() {
12            let Some(timpl) = ctx.translated.trait_impls.get_mut(impl_id) else {
13                continue;
14            };
15            let Some(tdecl) = ctx.translated.trait_decls.get(timpl.impl_trait.id) else {
16                continue;
17            };
18            if tdecl.methods.len() == timpl.methods.len() {
19                continue;
20            }
21
22            // A `TraitRef` that points to this impl with the correct generics.
23            let self_impl_ref = TraitImplRef {
24                id: timpl.def_id,
25                generics: Box::new(timpl.generics.identity_args()),
26            };
27            let self_predicate = TraitRef {
28                kind: TraitRefKind::TraitImpl(self_impl_ref.clone()),
29                trait_decl_ref: RegionBinder::empty(timpl.impl_trait.clone()),
30            };
31            // Map of methods we already have in the impl.
32            let mut methods_map: HashMap<TraitItemName, _> =
33                mem::take(&mut timpl.methods).into_iter().collect();
34            // Borrow shared to get access to the rest of the crate.
35            let timpl = ctx.translated.trait_impls.get(impl_id).unwrap();
36            let mut methods = vec![];
37            for bound_method in &tdecl.methods {
38                if let Some(kv) = methods_map.remove_entry(bound_method.name()) {
39                    methods.push(kv);
40                    continue;
41                }
42                let declared_fun_id = bound_method.skip_binder.item.id;
43                let declared_fun_name = ctx.translated.item_name(declared_fun_id).unwrap();
44                let new_fun_name = {
45                    let mut item_name = timpl.item_meta.name.clone();
46                    item_name
47                        .name
48                        .push(declared_fun_name.name.last().unwrap().clone());
49                    item_name
50                };
51                let opacity = ctx.opacity_for_name(&new_fun_name);
52                let new_fun_id = ctx.translated.fun_decls.reserve_slot();
53                ctx.translated
54                    .item_names
55                    .insert(new_fun_id.into(), new_fun_name.clone());
56
57                // Substitute the method reference to be valid in the context of the impl.
58                let bound_method = bound_method
59                    .clone()
60                    .substitute_with_self(&timpl.impl_trait.generics, &self_predicate.kind);
61                // The new function item has for params the concatenation of impl params and method
62                // params. We build a FunDeclRef to this even if we don't end up adding the new
63                // function item below.
64                let new_fn_ref = Binder {
65                    skip_binder: FunDeclRef {
66                        id: new_fun_id,
67                        generics: Box::new(
68                            timpl
69                                .generics
70                                .identity_args_at_depth(DeBruijnId::one())
71                                .concat(
72                                    &bound_method
73                                        .params
74                                        .identity_args_at_depth(DeBruijnId::zero()),
75                                ),
76                        ),
77                    },
78                    params: bound_method.params.clone(),
79                    kind: bound_method.kind.clone(),
80                };
81                methods.push((bound_method.name().clone(), new_fn_ref));
82
83                if let Some(fun_decl) = ctx.translated.fun_decls.get(declared_fun_id)
84                    && !opacity.is_invisible()
85                {
86                    let bound_method = Binder {
87                        params: timpl.generics.clone(),
88                        skip_binder: bound_method,
89                        kind: BinderKind::Other,
90                    };
91                    // Flatten into a single binder level. This gives us the concatenated
92                    // parameters that we'll use for the new function item, and the arguments to
93                    // pass to the old function item.
94                    let bound_method = bound_method.flatten();
95                    // Create a copy of the provided method and update all the relevant data.
96                    let FunDecl {
97                        def_id: _,
98                        item_meta,
99                        signature,
100                        kind,
101                        is_global_initializer,
102                        body,
103                    } = fun_decl.clone();
104                    // We use the span of the *impl*, not the span of the
105                    // default method in the original trait declaration.
106                    let span = timpl.item_meta.span;
107                    let item_meta = ItemMeta {
108                        name: new_fun_name,
109                        is_local: timpl.item_meta.is_local,
110                        opacity,
111                        span,
112                        ..item_meta
113                    };
114                    let signature = FunSig {
115                        generics: bound_method.params,
116                        inputs: signature.inputs.substitute_with_self(
117                            &bound_method.skip_binder.item.generics,
118                            &self_predicate.kind,
119                        ),
120                        output: signature.output.substitute_with_self(
121                            &bound_method.skip_binder.item.generics,
122                            &self_predicate.kind,
123                        ),
124                        ..signature
125                    };
126                    let kind = if let ItemKind::TraitDecl {
127                        trait_ref,
128                        item_name,
129                        ..
130                    } = kind
131                    {
132                        ItemKind::TraitImpl {
133                            impl_ref: self_impl_ref.clone(),
134                            trait_ref: trait_ref.substitute_with_self(
135                                &bound_method.skip_binder.item.generics,
136                                &self_predicate.kind,
137                            ),
138                            item_name,
139                            reuses_default: true,
140                        }
141                    } else {
142                        unreachable!()
143                    };
144                    let body = if opacity.is_transparent() {
145                        body.substitute_with_self(
146                            &bound_method.skip_binder.item.generics,
147                            &self_predicate.kind,
148                        )
149                    } else {
150                        Err(Opaque)
151                    };
152                    ctx.translated.fun_decls.set_slot(
153                        new_fun_id,
154                        FunDecl {
155                            def_id: new_fun_id,
156                            item_meta,
157                            signature,
158                            kind,
159                            is_global_initializer,
160                            body,
161                        },
162                    );
163                }
164            }
165            let timpl = ctx.translated.trait_impls.get_mut(impl_id).unwrap();
166            timpl.methods = methods;
167        }
168    }
169}