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::{ctx::TransformPass, TransformCtx};
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 (name, decl_fn_ref) in &tdecl.methods {
38                if let Some(kv) = methods_map.remove_entry(name) {
39                    methods.push(kv);
40                    continue;
41                }
42                let declared_fun_id = decl_fn_ref.skip_binder.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_fn = decl_fn_ref
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_fn.params.identity_args_at_depth(DeBruijnId::zero()),
73                                ),
74                        ),
75                    },
76                    params: bound_fn.params.clone(),
77                    kind: bound_fn.kind.clone(),
78                };
79                methods.push((name.clone(), new_fn_ref));
80
81                if let Some(fun_decl) = ctx.translated.fun_decls.get(declared_fun_id)
82                    && !opacity.is_invisible()
83                {
84                    let bound_fn = Binder {
85                        params: timpl.generics.clone(),
86                        skip_binder: bound_fn,
87                        kind: BinderKind::Other,
88                    };
89                    // Flatten into a single binder level. This gives us the concatenated
90                    // parameters that we'll use for the new function item, and the arguments to
91                    // pass to the old function item.
92                    let bound_fn = bound_fn.flatten();
93                    // Create a copy of the provided method and update all the relevant data.
94                    let FunDecl {
95                        def_id: _,
96                        item_meta,
97                        signature,
98                        kind,
99                        is_global_initializer,
100                        body,
101                    } = fun_decl.clone();
102                    // We use the span of the *impl*, not the span of the
103                    // default method in the original trait declaration.
104                    let span = timpl.item_meta.span;
105                    let item_meta = ItemMeta {
106                        name: new_fun_name,
107                        is_local: timpl.item_meta.is_local,
108                        opacity,
109                        span,
110                        ..item_meta
111                    };
112                    let signature = FunSig {
113                        generics: bound_fn.params,
114                        inputs: signature.inputs.substitute_with_self(
115                            &bound_fn.skip_binder.generics,
116                            &self_predicate.kind,
117                        ),
118                        output: signature.output.substitute_with_self(
119                            &bound_fn.skip_binder.generics,
120                            &self_predicate.kind,
121                        ),
122                        ..signature
123                    };
124                    let kind = if let ItemKind::TraitDecl {
125                        trait_ref,
126                        item_name,
127                        ..
128                    } = kind
129                    {
130                        ItemKind::TraitImpl {
131                            impl_ref: self_impl_ref.clone(),
132                            trait_ref: trait_ref.substitute_with_self(
133                                &bound_fn.skip_binder.generics,
134                                &self_predicate.kind,
135                            ),
136                            item_name,
137                            reuses_default: true,
138                        }
139                    } else {
140                        unreachable!()
141                    };
142                    let body = if opacity.is_transparent() {
143                        body.substitute_with_self(
144                            &bound_fn.skip_binder.generics,
145                            &self_predicate.kind,
146                        )
147                    } else {
148                        Err(Opaque)
149                    };
150                    ctx.translated.fun_decls.set_slot(
151                        new_fun_id,
152                        FunDecl {
153                            def_id: new_fun_id,
154                            item_meta,
155                            signature,
156                            kind,
157                            is_global_initializer,
158                            body,
159                        },
160                    );
161                }
162            }
163            let timpl = ctx.translated.trait_impls.get_mut(impl_id).unwrap();
164            timpl.methods = methods;
165        }
166    }
167}