flowistry_pdg_construction/
callback.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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
//! Callbacks to influence graph construction and their supporting types.

use flowistry_pdg::{rustc_portable::Location, CallString};

use rustc_middle::{
    mir,
    ty::{Instance, TypingEnv},
};
use rustc_span::Span;

use crate::{calling_convention::CallingConvention, utils::ArgSlice};

pub trait CallChangeCallback<'tcx> {
    fn on_inline(&self, info: CallInfo<'tcx, '_>) -> CallChanges<'tcx>;

    fn on_inline_miss(
        &self,
        _resolution: Instance<'tcx>,
        _param_env: TypingEnv<'tcx>,
        _loc: Location,
        _under_analysis: Instance<'tcx>,
        _reason: InlineMissReason,
        _call_span: Span,
    ) {
    }
}

pub struct CallChangeCallbackFn<'tcx> {
    f: Box<dyn Fn(CallInfo<'tcx, '_>) -> CallChanges<'tcx> + 'tcx>,
}

impl<'tcx> CallChangeCallbackFn<'tcx> {
    pub fn new(f: impl Fn(CallInfo<'tcx, '_>) -> CallChanges<'tcx> + 'tcx) -> Self {
        Self { f: Box::new(f) }
    }
}

impl<'tcx> CallChangeCallback<'tcx> for CallChangeCallbackFn<'tcx> {
    fn on_inline(&self, info: CallInfo<'tcx, '_>) -> CallChanges<'tcx> {
        (self.f)(info)
    }
}

#[derive(Debug)]
pub enum InlineMissReason {
    Async(String),
    TraitMethod,
}

impl Default for CallChanges<'_> {
    fn default() -> Self {
        CallChanges {
            skip: SkipCall::NoSkip,
        }
    }
}

/// Information about the function being called.
pub struct CallInfo<'tcx, 'mir> {
    /// The potentially-monomorphized resolution of the callee.
    pub callee: Instance<'tcx>,

    /// If the callee is an async closure created by an `async fn`, this is the
    /// `async fn` item.
    pub async_parent: Option<Instance<'tcx>>,

    /// The call-stack up to the current call site.
    pub call_string: CallString,

    /// Would the PDG for this function be served from the cache.
    pub is_cached: bool,

    pub span: Span,

    pub arguments: ArgSlice<'mir, 'tcx>,

    pub caller_body: &'mir mir::Body<'tcx>,
    pub param_env: TypingEnv<'tcx>,
}

/// User-provided changes to the default PDG construction behavior for function calls.
///
/// Construct [`CallChanges`] via [`CallChanges::default`].
#[derive(Debug)]
pub struct CallChanges<'tcx> {
    pub(crate) skip: SkipCall<'tcx>,
}

/// Whether or not to skip recursing into a function call during PDG construction.
#[derive(Debug)]
pub enum SkipCall<'tcx> {
    /// Skip the function, and perform a modular approxmation of its effects.
    Skip,

    /// Recurse into the function as normal.
    NoSkip,

    /// Replace with a call to this other function and arguments.
    Replace {
        instance: Instance<'tcx>,
        calling_convention: CallingConvention<'tcx>,
    },
}

impl<'tcx> CallChanges<'tcx> {
    /// Indicate whether or not to skip recursing into the given function.
    pub fn with_skip(self, skip: SkipCall<'tcx>) -> Self {
        CallChanges { skip }
    }
}