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;
}
}
}