nifty_check/checker/
env.rs1use std::collections::{BTreeSet, HashMap};
5
6use crate::hir::{FnId, LocalId, StateSet, Type};
7
8#[derive(Clone, Debug)]
10pub(super) enum Context {
11 Function { return_type: Type },
13 Ui,
15 Action,
17 StateInit,
19}
20
21#[derive(Clone, Debug)]
22pub(super) struct LocalInfo {
23 pub ty: Type,
24 pub kind: LocalKind,
25}
26
27#[derive(Clone, Copy, Debug, PartialEq, Eq)]
28pub(super) enum LocalKind {
29 Variable,
31 Alias,
33 Counter,
35}
36
37#[derive(Debug)]
39pub(super) struct Body {
40 pub locals: Vec<LocalInfo>,
41 scopes: Vec<HashMap<String, LocalId>>,
42 pub context: Context,
43}
44
45impl Body {
46 pub(super) fn new(context: Context) -> Self {
47 Self {
48 locals: Vec::new(),
49 scopes: vec![HashMap::new()],
50 context,
51 }
52 }
53
54 pub(super) fn declare(&mut self, name: &str, info: LocalInfo) -> LocalId {
56 let id = self.allocate(info);
57 self.bind(name, id);
58 id
59 }
60
61 pub(super) fn allocate(&mut self, info: LocalInfo) -> LocalId {
63 let id = LocalId(self.locals.len());
64 self.locals.push(info);
65 id
66 }
67
68 pub(super) fn bind(&mut self, name: &str, id: LocalId) {
69 self.scopes
70 .last_mut()
71 .expect("a body always has a scope")
72 .insert(name.to_owned(), id);
73 }
74
75 pub(super) fn lookup(&self, name: &str) -> Option<LocalId> {
76 self.scopes
77 .iter()
78 .rev()
79 .find_map(|scope| scope.get(name).copied())
80 }
81
82 pub(super) fn local(&self, id: LocalId) -> &LocalInfo {
83 &self.locals[id.0]
84 }
85
86 pub(super) fn push_scope(&mut self) {
87 self.scopes.push(HashMap::new());
88 }
89
90 pub(super) fn pop_scope(&mut self) {
91 self.scopes.pop();
92 }
93}
94
95#[derive(Debug, Default)]
97pub(super) struct Effects {
98 pub reads: StateSet,
99 pub writes: bool,
100 pub calls: BTreeSet<FnId>,
101}
102
103impl Effects {
104 pub(super) fn merge(&mut self, other: &Self) {
105 self.reads.extend(&other.reads);
106 self.writes |= other.writes;
107 self.calls.extend(other.calls.iter().copied());
108 }
109}
110
111#[derive(Debug, Default)]
113pub(super) struct FnEffects {
114 pub reads: Vec<StateSet>,
115 pub writes: Vec<bool>,
116}
117
118impl FnEffects {
119 pub(super) fn solve(direct: &[Effects]) -> Self {
121 let mut reads: Vec<StateSet> = direct.iter().map(|e| e.reads.clone()).collect();
122 let mut writes: Vec<bool> = direct.iter().map(|e| e.writes).collect();
123 let mut changed = true;
124 while changed {
125 changed = false;
126 for (caller, effects) in direct.iter().enumerate() {
127 for callee in &effects.calls {
128 let callee_reads = reads[callee.0].clone();
129 let before = reads[caller].clone();
130 reads[caller].extend(&callee_reads);
131 let callee_writes = writes[callee.0];
132 if reads[caller] != before || (callee_writes && !writes[caller]) {
133 writes[caller] |= callee_writes;
134 changed = true;
135 }
136 }
137 }
138 }
139 Self { reads, writes }
140 }
141
142 pub(super) fn total(&self, effects: &Effects) -> (StateSet, bool) {
144 let mut reads = effects.reads.clone();
145 let mut writes = effects.writes;
146 for callee in &effects.calls {
147 reads.extend(&self.reads[callee.0]);
148 writes |= self.writes[callee.0];
149 }
150 (reads, writes)
151 }
152}