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            kind: DropKind::Precise,
26            place,
27            tref,
28            target,
29            on_unwind,
30        } = &mut term.kind
31        {
32            // check if this drop is noop
33            if is_noop_destruct(tref) {
34                term.kind = TerminatorKind::Goto {
35                    target: target.clone(),
36                };
37                return;
38            }
39
40            // assign `&raw mut place` to a new variable
41            let drop_arg =
42                self.raw_borrow_to_new_var(place.clone(), RefKind::Mut, Some("drop_arg".into()));
43
44            // Get the declaration id of drop_in_place from tref
45            let trait_id = tref.trait_decl_ref.skip_binder.id;
46            let Some(tdecl) = self.ctx.translated.trait_decls.get(trait_id) else {
47                return;
48            };
49            let method_name = TraitItemName("drop_in_place".into());
50            let Some(bound_method) = tdecl.methods.iter().find(|m| m.name() == method_name) else {
51                // skip this drop if we cannot find its method id
52                return;
53            };
54            let method_decl_id = bound_method.skip_binder.item.id;
55
56            let drop_ret = self.fresh_var(Some("drop_ret".into()), Ty::mk_unit());
57            let fn_ptr = FnPtr::new(
58                FnPtrKind::Trait(tref.clone(), method_name, method_decl_id),
59                GenericArgs::empty(),
60            );
61            let call = Call {
62                func: FnOperand::Regular(fn_ptr),
63                args: Vec::from([Operand::Move(drop_arg)]),
64                dest: drop_ret,
65            };
66            term.kind = TerminatorKind::Call {
67                call,
68                target: target.clone(),
69                on_unwind: on_unwind.clone(),
70            };
71        }
72    }
73}
74
75pub struct Transform;
76
77impl UllbcPass for Transform {
78    fn transform_function(&self, ctx: &mut TransformCtx, decl: &mut FunDecl) {
79        if !ctx.options.desugar_drops {
80            return;
81        }
82        decl.transform_ullbc_terminators(ctx, |ctx, term| {
83            ctx.transform_drop_to_call(term);
84        });
85    }
86}