circomspect_program_structure/intermediate_representation/
statement_impl.rs

1use log::trace;
2use std::fmt;
3
4use super::declarations::Declarations;
5use super::ir::*;
6use super::degree_meta::{Degree, DegreeEnvironment, DegreeMeta};
7use super::type_meta::TypeMeta;
8use super::value_meta::{ValueEnvironment, ValueMeta};
9use super::variable_meta::{VariableMeta, VariableUse, VariableUses};
10
11impl Statement {
12    #[must_use]
13    pub fn meta(&self) -> &Meta {
14        use Statement::*;
15        match self {
16            Declaration { meta, .. }
17            | IfThenElse { meta, .. }
18            | Return { meta, .. }
19            | Substitution { meta, .. }
20            | LogCall { meta, .. }
21            | Assert { meta, .. }
22            | ConstraintEquality { meta, .. } => meta,
23        }
24    }
25
26    #[must_use]
27    pub fn meta_mut(&mut self) -> &mut Meta {
28        use Statement::*;
29        match self {
30            Declaration { meta, .. }
31            | IfThenElse { meta, .. }
32            | Return { meta, .. }
33            | Substitution { meta, .. }
34            | LogCall { meta, .. }
35            | Assert { meta, .. }
36            | ConstraintEquality { meta, .. } => meta,
37        }
38    }
39
40    pub fn propagate_degrees(&mut self, env: &mut DegreeEnvironment) -> bool {
41        let mut result = false;
42
43        use Degree::*;
44        use Statement::*;
45        use VariableType::*;
46        match self {
47            Declaration { names, var_type, .. } => {
48                for name in names.iter() {
49                    // Since we disregard accesses, components are treated as signals.
50                    if matches!(var_type, Signal(_, _) | Component | AnonymousComponent) {
51                        result = result || env.set_degree(name, &Linear.into());
52                    }
53                    env.set_type(name, var_type);
54                }
55                result
56            }
57            Substitution { var, rhe, .. } => {
58                result = result || rhe.propagate_degrees(env);
59                if env.is_local(var) {
60                    if let Some(range) = rhe.degree() {
61                        result = result || env.set_degree(var, range);
62                    }
63                }
64                result
65            }
66            LogCall { args, .. } => {
67                use LogArgument::*;
68                for arg in args {
69                    if let Expr(value) = arg {
70                        result = result || value.propagate_degrees(env);
71                    }
72                }
73                result
74            }
75            IfThenElse { cond, .. } => cond.propagate_degrees(env),
76            Return { value, .. } => value.propagate_degrees(env),
77            Assert { arg, .. } => arg.propagate_degrees(env),
78            ConstraintEquality { lhe, rhe, .. } => {
79                result = result || lhe.propagate_degrees(env);
80                result = result || rhe.propagate_degrees(env);
81                result
82            }
83        }
84    }
85
86    #[must_use]
87    pub fn propagate_values(&mut self, env: &mut ValueEnvironment) -> bool {
88        use Statement::*;
89        use Expression::*;
90        match self {
91            Declaration { dimensions, .. } => {
92                let mut result = false;
93                for size in dimensions {
94                    result = result || size.propagate_values(env);
95                }
96                result
97            }
98            Substitution { meta, var, rhe, .. } => {
99                let mut result = rhe.propagate_values(env);
100
101                // TODO: Handle array values.
102                if !matches!(rhe, Update { .. }) {
103                    if let Some(value) = rhe.value() {
104                        env.add_variable(var, value);
105                        result = result || meta.value_knowledge_mut().set_reduces_to(value.clone());
106                    }
107                }
108                trace!("Substitution returned {result}");
109                result
110            }
111            LogCall { args, .. } => {
112                let mut result = false;
113                use LogArgument::*;
114                for arg in args {
115                    if let Expr(value) = arg {
116                        result = result || value.propagate_values(env);
117                    }
118                }
119                result
120            }
121            IfThenElse { cond, .. } => cond.propagate_values(env),
122            Return { value, .. } => value.propagate_values(env),
123            Assert { arg, .. } => arg.propagate_values(env),
124            ConstraintEquality { lhe, rhe, .. } => {
125                lhe.propagate_values(env) || rhe.propagate_values(env)
126            }
127        }
128    }
129
130    pub fn propagate_types(&mut self, vars: &Declarations) {
131        use Statement::*;
132        match self {
133            Declaration { meta, var_type, dimensions, .. } => {
134                // The metadata tracks the type of the declared variable.
135                meta.type_knowledge_mut().set_variable_type(var_type);
136                for size in dimensions {
137                    size.propagate_types(vars);
138                }
139            }
140            Substitution { meta, var, rhe, .. } => {
141                // The metadata tracks the type of the assigned variable.
142                rhe.propagate_types(vars);
143                if let Some(var_type) = vars.get_type(var) {
144                    meta.type_knowledge_mut().set_variable_type(var_type);
145                }
146            }
147            LogCall { args, .. } => {
148                use LogArgument::*;
149                for arg in args {
150                    if let Expr(value) = arg {
151                        value.propagate_types(vars);
152                    }
153                }
154            }
155            ConstraintEquality { lhe, rhe, .. } => {
156                lhe.propagate_types(vars);
157                rhe.propagate_types(vars);
158            }
159            IfThenElse { cond, .. } => {
160                cond.propagate_types(vars);
161            }
162            Return { value, .. } => {
163                value.propagate_types(vars);
164            }
165            Assert { arg, .. } => {
166                arg.propagate_types(vars);
167            }
168        }
169    }
170}
171
172impl fmt::Debug for Statement {
173    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
174        use Statement::*;
175        match self {
176            Declaration { names, var_type, dimensions, .. } => {
177                write!(f, "{var_type} ")?;
178                let mut first = true;
179                for name in names {
180                    if first {
181                        first = false;
182                    } else {
183                        write!(f, ", ")?;
184                    }
185                    write!(f, "{name:?}")?;
186                    for size in dimensions {
187                        write!(f, "[{size:?}]")?;
188                    }
189                }
190                Ok(())
191            }
192            Substitution { var, op, rhe, .. } => write!(f, "{var:?} {op} {rhe:?}"),
193            ConstraintEquality { lhe, rhe, .. } => write!(f, "{lhe:?} === {rhe:?}"),
194            IfThenElse { cond, true_index, false_index, .. } => match false_index {
195                Some(false_index) => write!(f, "if {cond:?} then {true_index} else {false_index}"),
196                None => write!(f, "if {cond:?} then {true_index}"),
197            },
198            Return { value, .. } => write!(f, "return {value:?}"),
199            Assert { arg, .. } => write!(f, "assert({arg:?})"),
200            LogCall { args, .. } => write!(f, "log({:?})", vec_to_debug(args, ", ")),
201        }
202    }
203}
204
205impl fmt::Display for Statement {
206    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
207        use Statement::*;
208        match self {
209            Declaration { names, var_type, dimensions, .. } => {
210                // We rewrite declarations of multiple SSA variables as a single
211                // declaration of the original variable.
212                write!(f, "{var_type} {}", names.first())?;
213                for size in dimensions {
214                    write!(f, "[{size}]")?;
215                }
216                Ok(())
217            }
218            Substitution { var, op, rhe, .. } => {
219                match rhe {
220                    // We rewrite `Update` expressions of arrays/component signals.
221                    Expression::Update { access, rhe, .. } => {
222                        write!(f, "{var}")?;
223                        for access in access {
224                            write!(f, "{access}")?;
225                        }
226                        write!(f, " {op} {rhe}")
227                    }
228                    // This is an ordinary assignment.
229                    _ => write!(f, "{var} {op} {rhe}"),
230                }
231            }
232            ConstraintEquality { lhe, rhe, .. } => write!(f, "{lhe} === {rhe}"),
233            IfThenElse { cond, .. } => write!(f, "if {cond}"),
234            Return { value, .. } => write!(f, "return {value}"),
235            Assert { arg, .. } => write!(f, "assert({arg})"),
236            LogCall { args, .. } => write!(f, "log({})", vec_to_display(args, ", ")),
237        }
238    }
239}
240
241impl fmt::Display for AssignOp {
242    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
243        use AssignOp::*;
244        match self {
245            AssignSignal => write!(f, "<--"),
246            AssignConstraintSignal => write!(f, "<=="),
247            AssignLocalOrComponent => write!(f, "="),
248        }
249    }
250}
251
252impl fmt::Display for LogArgument {
253    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
254        use LogArgument::*;
255        match self {
256            String(message) => write!(f, "{message}"),
257            Expr(value) => write!(f, "{value}"),
258        }
259    }
260}
261
262impl fmt::Debug for LogArgument {
263    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
264        use LogArgument::*;
265        match self {
266            String(message) => write!(f, "{message:?}"),
267            Expr(value) => write!(f, "{value:?}"),
268        }
269    }
270}
271
272impl VariableMeta for Statement {
273    fn cache_variable_use(&mut self) {
274        let mut locals_read = VariableUses::new();
275        let mut locals_written = VariableUses::new();
276        let mut signals_read = VariableUses::new();
277        let mut signals_written = VariableUses::new();
278        let mut components_read = VariableUses::new();
279        let mut components_written = VariableUses::new();
280
281        use Statement::*;
282        use Expression::*;
283        match self {
284            Declaration { dimensions, .. } => {
285                for size in dimensions {
286                    size.cache_variable_use();
287                    locals_read.extend(size.locals_read().clone());
288                    signals_read.extend(size.signals_read().clone());
289                    components_read.extend(size.components_read().clone());
290                }
291            }
292            Substitution { meta, var, op, rhe } => {
293                rhe.cache_variable_use();
294                locals_read.extend(rhe.locals_read().clone());
295                signals_read.extend(rhe.signals_read().clone());
296                components_read.extend(rhe.components_read().clone());
297
298                let access = match rhe {
299                    Update { access, .. } => access.clone(),
300                    _ => Vec::new(),
301                };
302                match meta.type_knowledge().variable_type() {
303                    Some(VariableType::Local) => {
304                        trace!("adding `{var:?}` to local variables written");
305                        locals_written.insert(VariableUse::new(meta, var, &access));
306                    }
307                    Some(VariableType::Signal(_, _)) => {
308                        trace!("adding `{var:?}` to signals written");
309                        signals_written.insert(VariableUse::new(meta, var, &access));
310                        if matches!(op, AssignOp::AssignConstraintSignal) {
311                            // If this is a signal constraint assignment, we
312                            // consider the assigned signal to be read as well.
313                            trace!("adding `{var:?}` to signals read");
314                            signals_read.insert(VariableUse::new(meta, var, &access));
315                        }
316                    }
317                    Some(VariableType::Component | VariableType::AnonymousComponent) => {
318                        trace!("adding `{var:?}` to components written");
319                        components_written.insert(VariableUse::new(meta, var, &access));
320                    }
321                    None => {
322                        trace!("variable `{var:?}` of unknown type written");
323                    }
324                }
325            }
326            LogCall { args, .. } => {
327                use LogArgument::*;
328                for arg in args {
329                    if let Expr(value) = arg {
330                        value.cache_variable_use();
331                        locals_read.extend(value.locals_read().clone());
332                        signals_read.extend(value.signals_read().clone());
333                        components_read.extend(value.components_read().clone());
334                    }
335                }
336            }
337            IfThenElse { cond, .. } => {
338                cond.cache_variable_use();
339                locals_read.extend(cond.locals_read().clone());
340                signals_read.extend(cond.signals_read().clone());
341                components_read.extend(cond.components_read().clone());
342            }
343            Return { value, .. } => {
344                value.cache_variable_use();
345                locals_read.extend(value.locals_read().clone());
346                signals_read.extend(value.signals_read().clone());
347                components_read.extend(value.components_read().clone());
348            }
349            Assert { arg, .. } => {
350                arg.cache_variable_use();
351                locals_read.extend(arg.locals_read().clone());
352                signals_read.extend(arg.signals_read().clone());
353                components_read.extend(arg.components_read().clone());
354            }
355            ConstraintEquality { lhe, rhe, .. } => {
356                lhe.cache_variable_use();
357                rhe.cache_variable_use();
358                locals_read.extend(lhe.locals_read().iter().cloned());
359                locals_read.extend(rhe.locals_read().iter().cloned());
360                signals_read.extend(lhe.signals_read().iter().cloned());
361                signals_read.extend(rhe.signals_read().iter().cloned());
362                components_read.extend(lhe.components_read().iter().cloned());
363                components_read.extend(rhe.components_read().iter().cloned());
364            }
365        }
366        self.meta_mut()
367            .variable_knowledge_mut()
368            .set_locals_read(&locals_read)
369            .set_locals_written(&locals_written)
370            .set_signals_read(&signals_read)
371            .set_signals_written(&signals_written)
372            .set_components_read(&components_read)
373            .set_components_written(&components_written);
374    }
375
376    fn locals_read(&self) -> &VariableUses {
377        self.meta().variable_knowledge().locals_read()
378    }
379
380    fn locals_written(&self) -> &VariableUses {
381        self.meta().variable_knowledge().locals_written()
382    }
383
384    fn signals_read(&self) -> &VariableUses {
385        self.meta().variable_knowledge().signals_read()
386    }
387
388    fn signals_written(&self) -> &VariableUses {
389        self.meta().variable_knowledge().signals_written()
390    }
391
392    fn components_read(&self) -> &VariableUses {
393        self.meta().variable_knowledge().components_read()
394    }
395
396    fn components_written(&self) -> &VariableUses {
397        self.meta().variable_knowledge().components_written()
398    }
399}
400
401#[must_use]
402fn vec_to_debug<T: fmt::Debug>(elems: &[T], sep: &str) -> String {
403    elems.iter().map(|elem| format!("{elem:?}")).collect::<Vec<String>>().join(sep)
404}
405
406#[must_use]
407fn vec_to_display<T: fmt::Display>(elems: &[T], sep: &str) -> String {
408    elems.iter().map(|elem| format!("{elem}")).collect::<Vec<String>>().join(sep)
409}