Skip to main content

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