charon_lib/transform/
duplicate_defaulted_methods.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
//! Add missing methods to trait impls by duplicating the default method.
use std::{collections::HashMap, mem};

use crate::ast::*;

use super::{ctx::TransformPass, TransformCtx};

pub struct Transform;
impl TransformPass for Transform {
    fn transform_ctx(&self, ctx: &mut TransformCtx) {
        for impl_id in ctx.translated.trait_impls.all_indices() {
            let Some(timpl) = ctx.translated.trait_impls.get_mut(impl_id) else {
                continue;
            };
            let Some(tdecl) = ctx.translated.trait_decls.get(timpl.impl_trait.trait_id) else {
                continue;
            };
            if tdecl.methods.len() == timpl.methods.len() {
                continue;
            }

            // A `TraitRef` that points to this impl with the correct generics.
            let self_impl_ref = TraitImplRef {
                impl_id: timpl.def_id,
                generics: timpl
                    .generics
                    .identity_args(GenericsSource::item(timpl.def_id)),
            };
            let self_predicate = TraitRef {
                kind: TraitRefKind::TraitImpl(
                    self_impl_ref.impl_id,
                    self_impl_ref.generics.clone(),
                ),
                trait_decl_ref: RegionBinder::empty(timpl.impl_trait.clone()),
            };
            // Map of methods we already have in the impl.
            let mut methods_map: HashMap<TraitItemName, _> =
                mem::take(&mut timpl.methods).into_iter().collect();
            // Borrow shared to get access to the rest of the crate.
            let timpl = ctx.translated.trait_impls.get(impl_id).unwrap();
            let mut methods = vec![];
            for (name, decl_fn_ref) in &tdecl.methods {
                if let Some(kv) = methods_map.remove_entry(name) {
                    methods.push(kv);
                    continue;
                }
                let declared_fun_id = decl_fn_ref.skip_binder.id;
                let declared_fun_name = ctx.translated.item_name(declared_fun_id).unwrap();
                let new_fun_name = {
                    let mut item_name = timpl.item_meta.name.clone();
                    item_name
                        .name
                        .push(declared_fun_name.name.last().unwrap().clone());
                    item_name
                };
                let opacity = ctx.opacity_for_name(&new_fun_name);
                let new_fun_id = ctx.translated.fun_decls.reserve_slot();
                ctx.translated.all_ids.insert(new_fun_id.into());
                ctx.translated
                    .item_names
                    .insert(new_fun_id.into(), new_fun_name.clone());

                // Substitute the method reference to be valid in the context of the impl.
                let bound_fn = decl_fn_ref
                    .clone()
                    .substitute_with_self(&timpl.impl_trait.generics, &self_predicate.kind);
                // The new function item has for params the concatenation of impl params and method
                // params. We build a FunDeclRef to this even if we don't end up adding the new
                // function item below.
                let new_fn_ref = Binder {
                    skip_binder: FunDeclRef {
                        id: new_fun_id,
                        generics: timpl
                            .generics
                            .identity_args_at_depth(
                                GenericsSource::item(timpl.def_id),
                                DeBruijnId::one(),
                            )
                            .concat(
                                GenericsSource::item(new_fun_id),
                                &bound_fn.params.identity_args_at_depth(
                                    GenericsSource::Method(
                                        timpl.impl_trait.trait_id.into(),
                                        name.clone(),
                                    ),
                                    DeBruijnId::zero(),
                                ),
                            ),
                    },
                    params: bound_fn.params.clone(),
                    kind: bound_fn.kind.clone(),
                };
                methods.push((name.clone(), new_fn_ref));

                if let Some(fun_decl) = ctx.translated.fun_decls.get(declared_fun_id)
                    && !opacity.is_invisible()
                {
                    let bound_fn = Binder {
                        params: timpl.generics.clone(),
                        skip_binder: bound_fn,
                        kind: BinderKind::Other,
                    };
                    // Flatten into a single binder level. This gives us the concatenated
                    // parameters that we'll use for the new function item, and the arguments to
                    // pass to the old function item.
                    let bound_fn = bound_fn.flatten();
                    // Create a copy of the provided method and update all the relevant data.
                    let FunDecl {
                        def_id: _,
                        item_meta,
                        signature,
                        kind,
                        is_global_initializer,
                        body,
                    } = fun_decl.clone();
                    let item_meta = ItemMeta {
                        name: new_fun_name,
                        is_local: timpl.item_meta.is_local,
                        opacity,
                        ..item_meta
                    };
                    let signature = FunSig {
                        generics: bound_fn.params,
                        inputs: signature.inputs.substitute_with_self(
                            &bound_fn.skip_binder.generics,
                            &self_predicate.kind,
                        ),
                        output: signature.output.substitute_with_self(
                            &bound_fn.skip_binder.generics,
                            &self_predicate.kind,
                        ),
                        ..signature
                    };
                    let kind = if let ItemKind::TraitDecl {
                        trait_ref,
                        item_name,
                        ..
                    } = kind
                    {
                        ItemKind::TraitImpl {
                            impl_ref: self_impl_ref.clone(),
                            trait_ref: trait_ref.substitute_with_self(
                                &bound_fn.skip_binder.generics,
                                &self_predicate.kind,
                            ),
                            item_name,
                            reuses_default: true,
                        }
                    } else {
                        unreachable!()
                    };
                    let body = if opacity.is_transparent() {
                        body.substitute_with_self(
                            &bound_fn.skip_binder.generics,
                            &self_predicate.kind,
                        )
                    } else {
                        Err(Opaque)
                    };
                    ctx.translated.fun_decls.set_slot(
                        new_fun_id,
                        FunDecl {
                            def_id: new_fun_id,
                            item_meta,
                            signature,
                            kind,
                            is_global_initializer,
                            body,
                        },
                    );
                }
            }
            let timpl = ctx.translated.trait_impls.get_mut(impl_id).unwrap();
            timpl.methods = methods;
        }
    }
}