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
use rustc_hash::FxHashMap;

use crate::{block_id::BlockId, item_id::ItemId};

/// Declares how a variable is used by a function
#[derive(Debug, Default, Clone, Copy)]
pub enum FunctionParameter {
    /// Variable is not used at all by this function
    #[default]
    None,
    /// Variable is read by this function
    Read,
    /// Variable is written to by this function
    Write,
    /// Variable is read from and written to by this function
    ReadWrite,
}

impl FunctionParameter {
    pub fn is_read(self) -> bool {
        matches!(self, Self::Read | Self::ReadWrite)
    }

    pub fn is_write(self) -> bool {
        matches!(self, Self::Write | Self::ReadWrite)
    }

    pub fn write(self) -> Self {
        match self {
            FunctionParameter::None | FunctionParameter::Write => FunctionParameter::Write,
            FunctionParameter::ReadWrite | FunctionParameter::Read => FunctionParameter::ReadWrite,
        }
    }

    pub fn read(self) -> Self {
        match self {
            FunctionParameter::None | FunctionParameter::Read => FunctionParameter::Read,
            FunctionParameter::ReadWrite | FunctionParameter::Write => FunctionParameter::ReadWrite,
        }
    }
}

/// Stores how functions use variables as parameters.
#[derive(Debug, Default)]
pub struct FunctionParameters {
    parameters: FxHashMap<BlockId, FxHashMap<ItemId, FunctionParameter>>,
}

impl FunctionParameters {
    pub fn dependencies_for(
        &self,
        variable: ItemId,
    ) -> impl Iterator<Item = (BlockId, FunctionParameter)> + '_ {
        self.parameters
            .iter()
            .filter_map(move |(id, func)| func.get(&variable).map(|param| (*id, *param)))
    }

    pub fn is_dependency(&self, variable: ItemId) -> bool {
        self.dependencies_for(variable)
            .any(|(_, dep)| dep.is_read())
    }

    pub fn get(&mut self, function: BlockId, id: ItemId) -> FunctionParameter {
        *self
            .parameters
            .entry(function)
            .or_default()
            .entry(id)
            .or_default()
    }

    pub fn get_function_parameters(
        &self,
        function: BlockId,
    ) -> Option<&FxHashMap<ItemId, FunctionParameter>> {
        self.parameters.get(&function)
    }

    pub fn set_read(&mut self, function: BlockId, id: ItemId) {
        let param = self
            .parameters
            .entry(function)
            .or_default()
            .entry(id)
            .or_default();
        *param = param.read();
    }

    pub fn set_write(&mut self, function: BlockId, id: ItemId) {
        let param = self
            .parameters
            .entry(function)
            .or_default()
            .entry(id)
            .or_default();
        *param = param.write();
    }

    pub fn clear(&mut self) {
        for function in self.parameters.values_mut() {
            function.clear();
        }
    }
}