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        if matches!(vtable_decl.kind, TypeDeclKind::Opaque) {
71            return Ok(()); }
73
74        let method_field_name = format!("method_{}", method_name);
76        let Some((method_field_id, method_field)) =
77            vtable_decl.get_field_by_name(None, &method_field_name)
78        else {
79            let vtable_name = vtable_decl_ref.id.with_ctx(fmt_ctx).to_string();
80            raise_error!(
81                self.ctx,
82                self.span,
83                "Could not determine method index for {} in vtable {}",
84                method_name,
85                vtable_name
86            );
87        };
88        let method_ty = method_field
89            .ty
90            .clone()
91            .substitute(&vtable_decl_ref.generics);
92
93        if call.args.is_empty() {
95            raise_error!(self.ctx, self.span, "Dyn trait call has no arguments!");
96        }
97        let dyn_trait_place = match &call.args[0] {
98            Operand::Copy(place) | Operand::Move(place) => place,
99            Operand::Const(_) => {
100                panic!("Unexpected constant as receiver for dyn trait method call")
101            }
102        };
103
104        let vtable_ty = TyKind::Adt(vtable_decl_ref).into_ty();
106        let ptr_to_vtable_ty = Ty::new(TyKind::RawPtr(vtable_ty.clone(), RefKind::Shared));
107        let method_field_place = dyn_trait_place
108            .clone()
109            .project(ProjectionElem::PtrMetadata, ptr_to_vtable_ty)
110            .project(ProjectionElem::Deref, vtable_ty)
111            .project(
112                ProjectionElem::Field(FieldProjKind::Adt(vtable_decl_id, None), method_field_id),
113                method_ty,
114            );
115
116        call.func = FnOperand::Move(method_field_place);
118
119        Ok(())
120    }
121}
122
123pub struct Transform;
124
125impl UllbcPass for Transform {
126    fn transform_function(&self, ctx: &mut TransformCtx, decl: &mut FunDecl) {
127        decl.transform_ullbc_terminators(ctx, |ctx, term| {
128            if let TerminatorKind::Call { call, .. } = &mut term.kind {
129                let _ = ctx.transform_dyn_trait_call(call);
130            }
131        });
132    }
133}