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
//! 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::{calling_convention::CallingConvention, 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 }
    }
}