charon_lib/transform/normalize/
transform_dyn_trait_calls.rs1use super::super::ctx::UllbcPass;
21use crate::{
22 errors::Error,
23 formatter::IntoFormatter,
24 pretty::FmtWithCtx,
25 raise_error, register_error,
26 transform::{TransformCtx, ctx::UllbcStatementTransformCtx},
27 ullbc_ast::*,
28};
29
30impl<'a> UllbcStatementTransformCtx<'a> {
31 fn transform_dyn_trait_call(&mut self, call: &mut Call) -> Result<(), Error> {
33 let fmt_ctx = &self.ctx.into_fmt();
34
35 let FnOperand::Regular(fn_ptr) = &call.func else {
37 return Ok(()); };
39 let FnPtrKind::Trait(trait_ref, method_name, _) = &fn_ptr.kind else {
40 return Ok(()); };
42 let TraitRefKind::Dyn = &trait_ref.kind else {
43 return Ok(()); };
45 let trait_pred = trait_ref.trait_decl_ref.clone().erase();
46
47 let vtable_decl_ref: TypeDeclRef = {
49 let Some(trait_decl) = self.ctx.translated.trait_decls.get(trait_pred.id) else {
51 return Ok(()); };
53 let Some(vtable_ty) = &trait_decl.vtable else {
55 raise_error!(
56 self.ctx,
57 self.span,
58 "Found a `dyn Trait` method call for non-dyn-compatible trait `{}`!",
59 trait_pred.id.with_ctx(fmt_ctx)
60 );
61 };
62 vtable_ty
63 .clone()
64 .substitute_with_self(&trait_pred.generics, &trait_ref.kind)
65 };
66 let vtable_decl_id = *vtable_decl_ref.id.as_adt().unwrap();
67 let Some(vtable_decl) = self.ctx.translated.type_decls.get(vtable_decl_id) else {
68 return Ok(()); };
70
71 let method_field_name = format!("method_{}", method_name);
73 let Some((method_field_id, method_field)) =
74 vtable_decl.get_field_by_name(None, &method_field_name)
75 else {
76 let vtable_name = vtable_decl_ref.id.with_ctx(fmt_ctx).to_string();
77 raise_error!(
78 self.ctx,
79 self.span,
80 "Could not determine method index for {} in vtable {}",
81 method_name,
82 vtable_name
83 );
84 };
85 let method_ty = method_field
86 .ty
87 .clone()
88 .substitute(&vtable_decl_ref.generics);
89
90 if call.args.is_empty() {
92 raise_error!(self.ctx, self.span, "Dyn trait call has no arguments!");
93 }
94 let dyn_trait_place = match &call.args[0] {
95 Operand::Copy(place) | Operand::Move(place) => place,
96 Operand::Const(_) => {
97 panic!("Unexpected constant as receiver for dyn trait method call")
98 }
99 };
100
101 let vtable_ty = TyKind::Adt(vtable_decl_ref).into_ty();
103 let ptr_to_vtable_ty = Ty::new(TyKind::RawPtr(vtable_ty.clone(), RefKind::Shared));
104 let method_field_place = dyn_trait_place
105 .clone()
106 .project(ProjectionElem::PtrMetadata, ptr_to_vtable_ty)
107 .project(ProjectionElem::Deref, vtable_ty)
108 .project(
109 ProjectionElem::Field(FieldProjKind::Adt(vtable_decl_id, None), method_field_id),
110 method_ty,
111 );
112
113 call.func = FnOperand::Move(method_field_place);
115
116 Ok(())
117 }
118}
119
120pub struct Transform;
121
122impl UllbcPass for Transform {
123 fn transform_function(&self, ctx: &mut TransformCtx, decl: &mut FunDecl) {
124 decl.transform_ullbc_terminators(ctx, |ctx, term| {
125 if let TerminatorKind::Call { call, .. } = &mut term.kind {
126 let _ = ctx.transform_dyn_trait_call(call);
127 }
128 });
129 }
130}