charon_lib/transform/
inline_local_panic_functions.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
//! `panic!()` expands to:
//! ```ignore
//! fn panic_cold_explicit() -> ! {
//!     core::panicking::panic_explicit()
//! }
//! panic_cold_explicit()
//! ```
//! Which defines a new function each time. This pass recognizes these functions and replaces calls
//! to them by a `Panic` terminator.

use derive_visitor::{visitor_enter_fn_mut, DriveMut};
use std::collections::HashSet;

use super::{ctx::LlbcPass, TransformCtx};
use crate::{
    builtins,
    llbc_ast::{
        AbortKind, Call, FnOperand, FnPtr, FunId, FunIdOrTraitMethodRef, RawStatement, Statement,
    },
    names::Name,
};

pub struct Transform;
impl LlbcPass for Transform {
    fn transform_ctx(&self, ctx: &mut TransformCtx<'_>) {
        // Collect the functions that were generated by the `panic!` macro.
        let mut panic_fns = HashSet::new();
        ctx.for_each_fun_decl(|_ctx, decl, body| {
            if let Ok(body) = body {
                let body = body.as_structured().unwrap();
                // If the whole body is only a call to this specific panic function.
                if let [st] = body.body.statements.as_slice()
                    && let RawStatement::Abort(AbortKind::Panic(name)) = &st.content
                {
                    if name.equals_ref_name(builtins::EXPLICIT_PANIC_NAME) {
                        // FIXME: also check that the name of the function is
                        // `panic_cold_explicit`?
                        panic_fns.insert(decl.def_id);
                    }
                }
            }
        });

        let panic_name = Name::from_path(builtins::EXPLICIT_PANIC_NAME);
        let panic_statement = RawStatement::Abort(AbortKind::Panic(panic_name));

        // Replace each call to one such function with a `Panic`.
        ctx.for_each_structured_body(|_ctx, body| {
            body.body.drive_mut(&mut visitor_enter_fn_mut(
                |st: &mut Statement| match &mut st.content {
                    RawStatement::Call(Call {
                        func:
                            FnOperand::Regular(FnPtr {
                                func: FunIdOrTraitMethodRef::Fun(FunId::Regular(fun_id)),
                                ..
                            }),
                        ..
                    }) if panic_fns.contains(fun_id) => {
                        st.content = panic_statement.clone();
                    }
                    _ => {}
                },
            ));
        });

        // Remove these functions from the context.
        for id in &panic_fns {
            ctx.translated.fun_decls.remove(*id);
        }
    }
}