charon_lib/transform/normalize/
desugar_drops.rs

1use super::super::ctx::UllbcPass;
2use crate::{
3    transform::{
4        TransformCtx,
5        ctx::{BodyTransformCtx, UllbcStatementTransformCtx},
6    },
7    ullbc_ast::*,
8};
9
10fn is_noop_destruct(tref: &TraitRef) -> bool {
11    matches!(
12        &tref.kind,
13        TraitRefKind::BuiltinOrAuto {
14            builtin_data: BuiltinImplData::NoopDestruct,
15            ..
16        }
17    )
18}
19
20impl<'a> UllbcStatementTransformCtx<'a> {
21    /// Transform a Drop to a Call that calls the drop_in_place method.
22    /// If we cannot desugar this drop, we just leave it unchanged.
23    fn transform_drop_to_call(&mut self, term: &mut Terminator) {
24        if let TerminatorKind::Drop {
25            place,
26            tref,
27            target,
28            on_unwind,
29        } = &mut term.kind
30        {
31            // check if this drop is noop
32            if is_noop_destruct(tref) {
33                term.kind = TerminatorKind::Goto {
34                    target: target.clone(),
35                };
36                return;
37            }
38
39            let ref_drop_arg = TyKind::RawPtr(place.ty().clone(), RefKind::Mut).into_ty();
40            let drop_arg = self.fresh_var(Some("drop_arg".into()), ref_drop_arg);
41            let drop_ret = self.fresh_var(Some("drop_ret".into()), Ty::mk_unit());
42
43            let ptr_metadata = self.compute_place_metadata(&place);
44            let rval = Rvalue::RawPtr {
45                place: place.clone(),
46                kind: RefKind::Mut,
47                ptr_metadata: ptr_metadata,
48            };
49            // assign &raw mut place to drop_arg
50            self.insert_assn_stmt(drop_arg.clone(), rval);
51
52            // Get the declaration id of drop_in_place from tref
53            let trait_id = tref.trait_decl_ref.skip_binder.id;
54            let Some(tdecl) = self.ctx.translated.trait_decls.get(trait_id) else {
55                return;
56            };
57            let method_name = TraitItemName("drop_in_place".into());
58            let Some(bound_method) = tdecl.methods.iter().find(|m| m.name() == method_name) else {
59                // skip this drop if we cannot find its method id
60                return;
61            };
62            let method_decl_id = bound_method.skip_binder.item.id;
63
64            let fn_ptr = FnPtr::new(
65                FnPtrKind::Trait(tref.clone(), method_name, method_decl_id),
66                GenericArgs::empty(),
67            );
68            let call = Call {
69                func: FnOperand::Regular(fn_ptr),
70                args: Vec::from([Operand::Move(drop_arg)]),
71                dest: drop_ret,
72            };
73            term.kind = TerminatorKind::Call {
74                call,
75                target: target.clone(),
76                on_unwind: on_unwind.clone(),
77            };
78        }
79    }
80}
81
82pub struct Transform;
83
84impl UllbcPass for Transform {
85    fn transform_function(&self, ctx: &mut TransformCtx, decl: &mut FunDecl) {
86        if !ctx.options.desugar_drops {
87            return;
88        }
89        decl.transform_ullbc_terminators(ctx, |ctx, term| {
90            ctx.transform_drop_to_call(term);
91        });
92    }
93}