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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
//! Callbacks to influence graph construction and their supporting types.

use flowistry_pdg::rustc_portable::Location;

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

use crate::analysis::CallingConvention;
use crate::utils::ArgSlice;

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

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

    fn root_k(&self, info: Instance<'tcx>) -> K;
}

pub struct DefaultCallback;

impl<'tcx, K: Default> CallChangeCallback<'tcx, K> for DefaultCallback {
    fn on_inline(&self, _info: CallInfo<'tcx, '_, K>) -> CallChanges<'tcx, K> {
        CallChanges::default()
    }

    fn root_k(&self, _info: Instance<'tcx>) -> K {
        Default::default()
    }
}

pub struct CallChangeCallbackFn<'tcx, K> {
    #[allow(clippy::type_complexity)]
    f: Box<dyn Fn(CallInfo<'tcx, '_, K>) -> CallChanges<'tcx, K> + 'tcx>,
}

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

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

    fn root_k(&self, _info: Instance<'tcx>) -> K {
        Default::default()
    }
}

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

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

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

    pub cache_key: &'mir K,

    /// 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 current location
    pub call_string: Location,

    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, K> {
    pub(crate) skip: SkipCall<'tcx, K>,
}

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

    /// Recurse into the function as normal.
    NoSkip(K),

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

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