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
30fn transform_dyn_trait_call(
32 ctx: &mut UllbcStatementTransformCtx<'_>,
33 call: &mut Call,
34) -> Result<(), Error> {
35 let fmt_ctx = &ctx.ctx.into_fmt();
36
37 let FnOperand::Regular(fn_ptr) = &call.func else {
39 return Ok(()); };
41 let FnPtrKind::Trait(trait_ref, method_name, _) = &fn_ptr.kind else {
42 return Ok(()); };
44 let TraitRefKind::Dyn = &trait_ref.kind else {
45 return Ok(()); };
47
48 let vtable_decl_ref: TypeDeclRef = {
50 let Some(trait_decl) = ctx.ctx.translated.trait_decls.get(trait_ref.trait_id()) else {
52 return Ok(()); };
54 let Some(vtable_ty) = &trait_decl.vtable else {
56 raise_error!(
57 ctx.ctx,
58 ctx.span,
59 "Found a `dyn Trait` method call for non-dyn-compatible trait `{}`!",
60 trait_ref.trait_id().with_ctx(fmt_ctx)
61 );
62 };
63 vtable_ty.clone().substitute_with_tref(trait_ref)
64 };
65 let vtable_decl_id = *vtable_decl_ref.id.as_adt().unwrap();
66 let Some(vtable_decl) = ctx.ctx.translated.type_decls.get(vtable_decl_id) else {
67 return Ok(()); };
69 if matches!(vtable_decl.kind, TypeDeclKind::Opaque) {
70 return Ok(()); }
72
73 let method_field_name = format!("method_{}", method_name);
75 let Some((method_field_id, method_field)) =
76 vtable_decl.get_field_by_name(None, &method_field_name)
77 else {
78 let vtable_name = vtable_decl_ref.id.with_ctx(fmt_ctx).to_string();
79 raise_error!(
80 ctx.ctx,
81 ctx.span,
82 "Could not determine method index for {} in vtable {}",
83 method_name,
84 vtable_name
85 );
86 };
87 let method_ty = method_field
88 .ty
89 .clone()
90 .substitute(&vtable_decl_ref.generics);
91
92 if call.args.is_empty() {
94 raise_error!(ctx.ctx, ctx.span, "Dyn trait call has no arguments!");
95 }
96 let dyn_trait_place = match &call.args[0] {
97 Operand::Copy(place) | Operand::Move(place) => place,
98 Operand::Const(_) => {
99 panic!("Unexpected constant as receiver for dyn trait method call")
100 }
101 };
102
103 let vtable_ty = TyKind::Adt(vtable_decl_ref).into_ty();
105 let ptr_to_vtable_ty = Ty::new(TyKind::RawPtr(vtable_ty.clone(), RefKind::Shared));
106 let method_field_place = dyn_trait_place
107 .clone()
108 .project(ProjectionElem::PtrMetadata, ptr_to_vtable_ty)
109 .project(ProjectionElem::Deref, vtable_ty)
110 .project(
111 ProjectionElem::Field(FieldProjKind::Adt(vtable_decl_id, None), method_field_id),
112 method_ty,
113 );
114
115 call.func = FnOperand::Dynamic(Operand::Copy(method_field_place));
117
118 Ok(())
119}
120
121pub struct Transform;
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 _ = transform_dyn_trait_call(ctx, call);
127 }
128 });
129 }
130}