circomspect_program_structure/intermediate_representation/
expression_impl.rs

1use log::trace;
2use num_traits::Zero;
3use std::collections::HashSet;
4use std::fmt;
5use std::hash::{Hash, Hasher};
6
7use circom_algebra::modular_arithmetic;
8
9use super::declarations::Declarations;
10use super::degree_meta::{Degree, DegreeEnvironment, DegreeMeta, DegreeRange};
11use super::ir::*;
12use super::type_meta::TypeMeta;
13use super::value_meta::{ValueEnvironment, ValueMeta, ValueReduction};
14use super::variable_meta::{VariableMeta, VariableUse, VariableUses};
15
16impl Expression {
17    #[must_use]
18    pub fn meta(&self) -> &Meta {
19        use Expression::*;
20        match self {
21            InfixOp { meta, .. }
22            | PrefixOp { meta, .. }
23            | SwitchOp { meta, .. }
24            | Variable { meta, .. }
25            | Number(meta, ..)
26            | Call { meta, .. }
27            | InlineArray { meta, .. }
28            | Update { meta, .. }
29            | Access { meta, .. }
30            | Phi { meta, .. } => meta,
31        }
32    }
33
34    #[must_use]
35    pub fn meta_mut(&mut self) -> &mut Meta {
36        use Expression::*;
37        match self {
38            InfixOp { meta, .. }
39            | PrefixOp { meta, .. }
40            | SwitchOp { meta, .. }
41            | Variable { meta, .. }
42            | Number(meta, ..)
43            | Call { meta, .. }
44            | InlineArray { meta, .. }
45            | Update { meta, .. }
46            | Access { meta, .. }
47            | Phi { meta, .. } => meta,
48        }
49    }
50}
51
52/// Syntactic equality for expressions.
53impl PartialEq for Expression {
54    fn eq(&self, other: &Expression) -> bool {
55        use Expression::*;
56        match (self, other) {
57            (
58                InfixOp { lhe: self_lhe, infix_op: self_op, rhe: self_rhe, .. },
59                InfixOp { lhe: other_lhe, infix_op: other_op, rhe: other_rhe, .. },
60            ) => self_op == other_op && self_lhe == other_lhe && self_rhe == other_rhe,
61            (
62                PrefixOp { prefix_op: self_op, rhe: self_rhe, .. },
63                PrefixOp { prefix_op: other_op, rhe: other_rhe, .. },
64            ) => self_op == other_op && self_rhe == other_rhe,
65            (
66                SwitchOp { cond: self_cond, if_true: self_true, if_false: self_false, .. },
67                SwitchOp { cond: other_cond, if_true: other_true, if_false: other_false, .. },
68            ) => self_cond == other_cond && self_true == other_true && self_false == other_false,
69            (Variable { name: self_name, .. }, Variable { name: other_name, .. }) => {
70                self_name == other_name
71            }
72            (Number(_, self_value), Number(_, other_value)) => self_value == other_value,
73            (
74                Call { name: self_id, args: self_args, .. },
75                Call { name: other_id, args: other_args, .. },
76            ) => self_id == other_id && self_args == other_args,
77            (InlineArray { values: self_values, .. }, InlineArray { values: other_values, .. }) => {
78                self_values == other_values
79            }
80            (
81                Update { var: self_var, access: self_access, rhe: self_rhe, .. },
82                Update { var: other_var, access: other_access, rhe: other_rhe, .. },
83            ) => self_var == other_var && self_access == other_access && self_rhe == other_rhe,
84            (
85                Access { var: self_var, access: self_access, .. },
86                Access { var: other_var, access: other_access, .. },
87            ) => self_var == other_var && self_access == other_access,
88            (Phi { args: self_args, .. }, Phi { args: other_args, .. }) => self_args == other_args,
89            _ => false,
90        }
91    }
92}
93
94impl Eq for Expression {}
95
96impl Hash for Expression {
97    fn hash<H: Hasher>(&self, state: &mut H) {
98        use Expression::*;
99        match self {
100            InfixOp { lhe, rhe, .. } => {
101                lhe.hash(state);
102                rhe.hash(state);
103            }
104            PrefixOp { rhe, .. } => {
105                rhe.hash(state);
106            }
107            SwitchOp { cond, if_true, if_false, .. } => {
108                cond.hash(state);
109                if_true.hash(state);
110                if_false.hash(state);
111            }
112            Variable { name, .. } => {
113                name.hash(state);
114            }
115            Call { args, .. } => {
116                args.hash(state);
117            }
118            InlineArray { values, .. } => {
119                values.hash(state);
120            }
121            Access { var, access, .. } => {
122                var.hash(state);
123                access.hash(state);
124            }
125            Update { var, access, rhe, .. } => {
126                var.hash(state);
127                access.hash(state);
128                rhe.hash(state);
129            }
130            Phi { args, .. } => {
131                args.hash(state);
132            }
133            Number(_, value) => {
134                value.hash(state);
135            }
136        }
137    }
138}
139
140impl DegreeMeta for Expression {
141    fn propagate_degrees(&mut self, env: &DegreeEnvironment) -> bool {
142        let mut result = false;
143
144        use Degree::*;
145        use Expression::*;
146        match self {
147            InfixOp { meta, lhe, rhe, infix_op } => {
148                result = result || lhe.propagate_degrees(env);
149                result = result || rhe.propagate_degrees(env);
150                let range = infix_op.propagate_degrees(lhe.degree(), rhe.degree());
151                if let Some(range) = range {
152                    result = result || meta.degree_knowledge_mut().set_degree(&range);
153                }
154                result
155            }
156            PrefixOp { meta, rhe, prefix_op, .. } => {
157                result = result || rhe.propagate_degrees(env);
158                let range = prefix_op.propagate_degrees(rhe.degree());
159                if let Some(range) = range {
160                    result = result || meta.degree_knowledge_mut().set_degree(&range);
161                }
162                result
163            }
164            SwitchOp { meta, cond, if_true, if_false, .. } => {
165                // If the condition has constant degree, the expression can be
166                // desugared using an if-statement and the maximum degree in
167                // each case will be the maximum of the individual if- and
168                // else-case degrees.
169                result = result || cond.propagate_degrees(env);
170                result = result || if_true.propagate_degrees(env);
171                result = result || if_false.propagate_degrees(env);
172                let Some(range) = cond.degree() else {
173                    return result;
174                };
175                if range.is_constant() {
176                    // The condition has constant degree.
177                    if let Some(range) =
178                        DegreeRange::iter_opt([if_true.degree(), if_false.degree()])
179                    {
180                        result = result || meta.degree_knowledge_mut().set_degree(&range);
181                    }
182                }
183                result
184            }
185            Variable { meta, name } => {
186                if let Some(range) = env.degree(name) {
187                    result = result || meta.degree_knowledge_mut().set_degree(range);
188                }
189                result
190            }
191            Call { meta, args, .. } => {
192                for arg in args.iter_mut() {
193                    result = result || arg.propagate_degrees(env);
194                }
195                // If one or more non-constant arguments is passed to the function we cannot
196                // say anything about the degree of the output. If the function only takes
197                // constant arguments the output must also be constant.
198                if args.iter().all(|arg| {
199                    if let Some(range) = arg.degree() {
200                        range.is_constant()
201                    } else {
202                        false
203                    }
204                }) {
205                    result = result || meta.degree_knowledge_mut().set_degree(&Constant.into())
206                }
207                result
208            }
209            InlineArray { meta, values } => {
210                // The degree range of an array is the infimum of the ranges of all elements.
211                for value in values.iter_mut() {
212                    result = result || value.propagate_degrees(env);
213                }
214                let range = DegreeRange::iter_opt(values.iter().map(|value| value.degree()));
215                if let Some(range) = range {
216                    result = result || meta.degree_knowledge_mut().set_degree(&range);
217                }
218                result
219            }
220            Access { meta, var, access } => {
221                // Accesses are ignored when determining the degree of a variable.
222                for access in access.iter_mut() {
223                    if let AccessType::ArrayAccess(index) = access {
224                        result = result || index.propagate_degrees(env);
225                    }
226                }
227                if let Some(range) = env.degree(var) {
228                    result = result || meta.degree_knowledge_mut().set_degree(range);
229                }
230                result
231            }
232            Update { meta, var, access, rhe, .. } => {
233                // Accesses are ignored when determining the degree of a variable.
234                result = result || rhe.propagate_degrees(env);
235                for access in access.iter_mut() {
236                    if let AccessType::ArrayAccess(index) = access {
237                        result = result || index.propagate_degrees(env);
238                    }
239                }
240                if env.degree(var).is_none() {
241                    // This is the first assignment to the array. The degree is given by the RHS.
242                    if let Some(range) = rhe.degree() {
243                        result = result || meta.degree_knowledge_mut().set_degree(range);
244                    }
245                } else {
246                    // The array has been assigned to previously. The degree is the infimum of
247                    // the degrees of `var` and the RHS.
248                    let range = DegreeRange::iter_opt([env.degree(var), rhe.degree()]);
249                    if let Some(range) = range {
250                        result = result || meta.degree_knowledge_mut().set_degree(&range);
251                    }
252                }
253                result
254            }
255            Phi { meta, args } => {
256                // The degree range of a phi expression is the infimum of the ranges of all the arguments.
257                let range = DegreeRange::iter_opt(args.iter().map(|arg| env.degree(arg)));
258                if let Some(range) = range {
259                    result = result || meta.degree_knowledge_mut().set_degree(&range);
260                }
261                result
262            }
263            Number(meta, _) => {
264                // Constants have constant degree.
265                meta.degree_knowledge_mut().set_degree(&Constant.into())
266            }
267        }
268    }
269
270    fn degree(&self) -> Option<&DegreeRange> {
271        self.meta().degree_knowledge().degree()
272    }
273}
274
275impl TypeMeta for Expression {
276    fn propagate_types(&mut self, vars: &Declarations) {
277        use Expression::*;
278        match self {
279            InfixOp { lhe, rhe, .. } => {
280                lhe.propagate_types(vars);
281                rhe.propagate_types(vars);
282            }
283            PrefixOp { rhe, .. } => {
284                rhe.propagate_types(vars);
285            }
286            SwitchOp { cond, if_true, if_false, .. } => {
287                cond.propagate_types(vars);
288                if_true.propagate_types(vars);
289                if_false.propagate_types(vars);
290            }
291            Variable { meta, name } => {
292                if let Some(var_type) = vars.get_type(name) {
293                    meta.type_knowledge_mut().set_variable_type(var_type);
294                }
295            }
296            Call { args, .. } => {
297                for arg in args {
298                    arg.propagate_types(vars);
299                }
300            }
301            InlineArray { values, .. } => {
302                for value in values {
303                    value.propagate_types(vars);
304                }
305            }
306            Access { meta, var, access } => {
307                for access in access.iter_mut() {
308                    if let AccessType::ArrayAccess(index) = access {
309                        index.propagate_types(vars);
310                    }
311                }
312                if let Some(var_type) = vars.get_type(var) {
313                    meta.type_knowledge_mut().set_variable_type(var_type);
314                }
315            }
316            Update { meta, var, access, rhe, .. } => {
317                rhe.propagate_types(vars);
318                for access in access.iter_mut() {
319                    if let AccessType::ArrayAccess(index) = access {
320                        index.propagate_types(vars);
321                    }
322                }
323                if let Some(var_type) = vars.get_type(var) {
324                    meta.type_knowledge_mut().set_variable_type(var_type);
325                }
326            }
327            Phi { .. } => {
328                // All phi node arguments are local variables.
329            }
330            Number(_, _) => {}
331        }
332    }
333
334    fn is_local(&self) -> bool {
335        self.meta().type_knowledge().is_local()
336    }
337
338    fn is_signal(&self) -> bool {
339        self.meta().type_knowledge().is_signal()
340    }
341
342    fn is_component(&self) -> bool {
343        self.meta().type_knowledge().is_component()
344    }
345
346    fn variable_type(&self) -> Option<&VariableType> {
347        self.meta().type_knowledge().variable_type()
348    }
349}
350
351impl VariableMeta for Expression {
352    fn cache_variable_use(&mut self) {
353        let mut locals_read = VariableUses::new();
354        let mut signals_read = VariableUses::new();
355        let mut components_read = VariableUses::new();
356
357        use Expression::*;
358        match self {
359            InfixOp { lhe, rhe, .. } => {
360                lhe.cache_variable_use();
361                rhe.cache_variable_use();
362                locals_read.extend(lhe.locals_read().iter().cloned());
363                locals_read.extend(rhe.locals_read().iter().cloned());
364                signals_read.extend(lhe.signals_read().iter().cloned());
365                signals_read.extend(rhe.signals_read().iter().cloned());
366                components_read.extend(lhe.components_read().iter().cloned());
367                components_read.extend(rhe.components_read().iter().cloned());
368            }
369            PrefixOp { rhe, .. } => {
370                rhe.cache_variable_use();
371                locals_read.extend(rhe.locals_read().iter().cloned());
372                signals_read.extend(rhe.signals_read().iter().cloned());
373                components_read.extend(rhe.components_read().iter().cloned());
374            }
375            SwitchOp { cond, if_true, if_false, .. } => {
376                cond.cache_variable_use();
377                if_true.cache_variable_use();
378                if_false.cache_variable_use();
379                locals_read.extend(cond.locals_read().clone());
380                locals_read.extend(if_true.locals_read().clone());
381                locals_read.extend(if_false.locals_read().clone());
382                signals_read.extend(cond.signals_read().clone());
383                signals_read.extend(if_true.signals_read().clone());
384                signals_read.extend(if_false.signals_read().clone());
385                components_read.extend(cond.components_read().clone());
386                components_read.extend(if_true.components_read().clone());
387                components_read.extend(if_false.components_read().clone());
388            }
389            Variable { meta, name } => {
390                match meta.type_knowledge().variable_type() {
391                    Some(VariableType::Local) => {
392                        trace!("adding `{name:?}` to local variables read");
393                        locals_read.insert(VariableUse::new(meta, name, &Vec::new()));
394                    }
395                    Some(VariableType::Component | VariableType::AnonymousComponent) => {
396                        trace!("adding `{name:?}` to components read");
397                        components_read.insert(VariableUse::new(meta, name, &Vec::new()));
398                    }
399                    Some(VariableType::Signal(_, _)) => {
400                        trace!("adding `{name:?}` to signals read");
401                        signals_read.insert(VariableUse::new(meta, name, &Vec::new()));
402                    }
403                    None => {
404                        // If the variable type is unknown we ignore it.
405                        trace!("variable `{name:?}` of unknown type read");
406                    }
407                }
408            }
409            Call { args, .. } => {
410                for arg in args {
411                    arg.cache_variable_use();
412                    locals_read.extend(arg.locals_read().clone());
413                    signals_read.extend(arg.signals_read().clone());
414                    components_read.extend(arg.components_read().clone());
415                }
416            }
417            Phi { meta, args } => {
418                locals_read
419                    .extend(args.iter().map(|name| VariableUse::new(meta, name, &Vec::new())));
420            }
421            InlineArray { values, .. } => {
422                for value in values {
423                    value.cache_variable_use();
424                    locals_read.extend(value.locals_read().clone());
425                    signals_read.extend(value.signals_read().clone());
426                    components_read.extend(value.components_read().clone());
427                }
428            }
429            Access { meta, var, access } => {
430                // Cache array index variable use.
431                for access in access.iter_mut() {
432                    if let AccessType::ArrayAccess(index) = access {
433                        index.cache_variable_use();
434                        locals_read.extend(index.locals_read().clone());
435                        signals_read.extend(index.signals_read().clone());
436                        components_read.extend(index.components_read().clone());
437                    }
438                }
439                // Match against the type of `var`.
440                match meta.type_knowledge().variable_type() {
441                    Some(VariableType::Local) => {
442                        trace!("adding `{var:?}` to local variables read");
443                        locals_read.insert(VariableUse::new(meta, var, access));
444                    }
445                    Some(VariableType::Component | VariableType::AnonymousComponent) => {
446                        trace!("adding `{var:?}` to components read");
447                        components_read.insert(VariableUse::new(meta, var, access));
448                    }
449                    Some(VariableType::Signal(_, _)) => {
450                        trace!("adding `{var:?}` to signals read");
451                        signals_read.insert(VariableUse::new(meta, var, access));
452                    }
453                    None => {
454                        // If the variable type is unknown we ignore it.
455                        trace!("variable `{var:?}` of unknown type read");
456                    }
457                }
458            }
459            Update { meta, var, access, rhe, .. } => {
460                // Cache RHS variable use.
461                rhe.cache_variable_use();
462                locals_read.extend(rhe.locals_read().iter().cloned());
463                signals_read.extend(rhe.signals_read().iter().cloned());
464                components_read.extend(rhe.components_read().iter().cloned());
465
466                // Cache array index variable use.
467                for access in access.iter_mut() {
468                    if let AccessType::ArrayAccess(index) = access {
469                        index.cache_variable_use();
470                        locals_read.extend(index.locals_read().clone());
471                        signals_read.extend(index.signals_read().clone());
472                        components_read.extend(index.components_read().clone());
473                    }
474                }
475                // Match against the type of `var`.
476                match meta.type_knowledge().variable_type() {
477                    Some(VariableType::Local) => {
478                        trace!("adding `{var:?}` to local variables read");
479                        locals_read.insert(VariableUse::new(meta, var, &Vec::new()));
480                    }
481                    Some(VariableType::Component | VariableType::AnonymousComponent) => {
482                        trace!("adding `{var:?}` to components read");
483                        components_read.insert(VariableUse::new(meta, var, &Vec::new()));
484                    }
485                    Some(VariableType::Signal(_, _)) => {
486                        trace!("adding `{var:?}` to signals read");
487                        signals_read.insert(VariableUse::new(meta, var, &Vec::new()));
488                    }
489                    None => {
490                        // If the variable type is unknown we ignore it.
491                        trace!("variable `{var:?}` of unknown type read");
492                    }
493                }
494            }
495            Number(_, _) => {}
496        }
497        self.meta_mut()
498            .variable_knowledge_mut()
499            .set_locals_read(&locals_read)
500            .set_locals_written(&VariableUses::new())
501            .set_signals_read(&signals_read)
502            .set_signals_written(&VariableUses::new())
503            .set_components_read(&components_read)
504            .set_components_written(&VariableUses::new());
505    }
506
507    fn locals_read(&self) -> &VariableUses {
508        self.meta().variable_knowledge().locals_read()
509    }
510
511    fn locals_written(&self) -> &VariableUses {
512        self.meta().variable_knowledge().locals_written()
513    }
514
515    fn signals_read(&self) -> &VariableUses {
516        self.meta().variable_knowledge().signals_read()
517    }
518
519    fn signals_written(&self) -> &VariableUses {
520        self.meta().variable_knowledge().signals_written()
521    }
522
523    fn components_read(&self) -> &VariableUses {
524        self.meta().variable_knowledge().components_read()
525    }
526
527    fn components_written(&self) -> &VariableUses {
528        self.meta().variable_knowledge().components_written()
529    }
530}
531
532impl ValueMeta for Expression {
533    fn propagate_values(&mut self, env: &mut ValueEnvironment) -> bool {
534        use Expression::*;
535        use ValueReduction::*;
536        match self {
537            InfixOp { meta, lhe, infix_op, rhe, .. } => {
538                let mut result = lhe.propagate_values(env) || rhe.propagate_values(env);
539                if let Some(value) = infix_op.propagate_values(lhe.value(), rhe.value(), env) {
540                    result = result || meta.value_knowledge_mut().set_reduces_to(value)
541                }
542                result
543            }
544            PrefixOp { meta, prefix_op, rhe } => {
545                let mut result = rhe.propagate_values(env);
546                if let Some(value) = prefix_op.propagate_values(rhe.value(), env) {
547                    result = result || meta.value_knowledge_mut().set_reduces_to(value)
548                }
549                result
550            }
551            SwitchOp { meta, cond, if_true, if_false } => {
552                let mut result = cond.propagate_values(env)
553                    | if_true.propagate_values(env)
554                    | if_false.propagate_values(env);
555                match (cond.value(), if_true.value(), if_false.value()) {
556                    (
557                        // The case true? value: _
558                        Some(Boolean { value: cond }),
559                        Some(value),
560                        _,
561                    ) if *cond => {
562                        result = result || meta.value_knowledge_mut().set_reduces_to(value.clone())
563                    }
564                    (
565                        // The case false? _: value
566                        Some(Boolean { value: cond }),
567                        _,
568                        Some(value),
569                    ) if !cond => {
570                        result = result || meta.value_knowledge_mut().set_reduces_to(value.clone())
571                    }
572                    (
573                        // The case true? value: _
574                        Some(FieldElement { value: cond }),
575                        Some(value),
576                        _,
577                    ) if !cond.is_zero() => {
578                        result = result || meta.value_knowledge_mut().set_reduces_to(value.clone())
579                    }
580                    (
581                        // The case false? _: value
582                        Some(FieldElement { value: cond }),
583                        _,
584                        Some(value),
585                    ) if cond.is_zero() => {
586                        result = result || meta.value_knowledge_mut().set_reduces_to(value.clone())
587                    }
588                    _ => {}
589                }
590                result
591            }
592            Variable { meta, name, .. } => match env.get_variable(name) {
593                Some(value) => meta.value_knowledge_mut().set_reduces_to(value.clone()),
594                None => false,
595            },
596            Number(meta, value) => {
597                let value = FieldElement { value: value.clone() };
598                meta.value_knowledge_mut().set_reduces_to(value)
599            }
600            Call { args, .. } => {
601                // TODO: Handle function calls.
602                let mut result = false;
603                for arg in args {
604                    result = result || arg.propagate_values(env);
605                }
606                result
607            }
608            InlineArray { values, .. } => {
609                // TODO: Handle inline arrays.
610                let mut result = false;
611                for value in values {
612                    result = result || value.propagate_values(env);
613                }
614                result
615            }
616            Access { access, .. } => {
617                // TODO: Handle array values.
618                let mut result = false;
619                for access in access.iter_mut() {
620                    if let AccessType::ArrayAccess(index) = access {
621                        result = result || index.propagate_values(env);
622                    }
623                }
624                result
625            }
626            Update { access, rhe, .. } => {
627                // TODO: Handle array values.
628                let mut result = rhe.propagate_values(env);
629                for access in access.iter_mut() {
630                    if let AccessType::ArrayAccess(index) = access {
631                        result = result || index.propagate_values(env);
632                    }
633                }
634                result
635            }
636            Phi { meta, args, .. } => {
637                // Only set the value of the phi expression if all arguments agree on the value.
638                let values =
639                    args.iter().map(|name| env.get_variable(name)).collect::<Option<HashSet<_>>>();
640                match values {
641                    Some(values) if values.len() == 1 => {
642                        // This unwrap is safe since the size is non-zero.
643                        let value = *values.iter().next().unwrap();
644                        meta.value_knowledge_mut().set_reduces_to(value.clone())
645                    }
646                    _ => false,
647                }
648            }
649        }
650    }
651
652    fn is_constant(&self) -> bool {
653        self.value().is_some()
654    }
655
656    fn is_boolean(&self) -> bool {
657        matches!(self.value(), Some(ValueReduction::Boolean { .. }))
658    }
659
660    fn is_field_element(&self) -> bool {
661        matches!(self.value(), Some(ValueReduction::FieldElement { .. }))
662    }
663
664    fn value(&self) -> Option<&ValueReduction> {
665        self.meta().value_knowledge().get_reduces_to()
666    }
667}
668
669impl ExpressionInfixOpcode {
670    fn propagate_degrees(
671        &self,
672        lhr: Option<&DegreeRange>,
673        rhr: Option<&DegreeRange>,
674    ) -> Option<DegreeRange> {
675        if let (Some(lhr), Some(rhr)) = (lhr, rhr) {
676            use ExpressionInfixOpcode::*;
677            match self {
678                Add => Some(lhr.add(rhr)),
679                Sub => Some(lhr.infix_sub(rhr)),
680                Mul => Some(lhr.mul(rhr)),
681                Pow => Some(lhr.pow(rhr)),
682                Div => Some(lhr.div(rhr)),
683                IntDiv => Some(lhr.int_div(rhr)),
684                Mod => Some(lhr.modulo(rhr)),
685                ShiftL => Some(lhr.shift_left(rhr)),
686                ShiftR => Some(lhr.shift_right(rhr)),
687                Lesser => Some(lhr.lesser(rhr)),
688                Greater => Some(lhr.greater(rhr)),
689                LesserEq => Some(lhr.lesser_eq(rhr)),
690                GreaterEq => Some(lhr.greater_eq(rhr)),
691                Eq => Some(lhr.equal(rhr)),
692                NotEq => Some(lhr.not_equal(rhr)),
693                BitOr => Some(lhr.bit_or(rhr)),
694                BitXor => Some(lhr.bit_xor(rhr)),
695                BitAnd => Some(lhr.bit_and(rhr)),
696                BoolOr => Some(lhr.bool_or(rhr)),
697                BoolAnd => Some(lhr.bool_and(rhr)),
698            }
699        } else {
700            None
701        }
702    }
703
704    fn propagate_values(
705        &self,
706        lhv: Option<&ValueReduction>,
707        rhv: Option<&ValueReduction>,
708        env: &ValueEnvironment,
709    ) -> Option<ValueReduction> {
710        let p = env.prime();
711
712        use ValueReduction::*;
713        match (lhv, rhv) {
714            // lhv and rhv reduce to two field elements.
715            (Some(FieldElement { value: lhv }), Some(FieldElement { value: rhv })) => {
716                use ExpressionInfixOpcode::*;
717                match self {
718                    Mul => {
719                        let value = modular_arithmetic::mul(lhv, rhv, p);
720                        Some(FieldElement { value })
721                    }
722                    Div => modular_arithmetic::div(lhv, rhv, p)
723                        .ok()
724                        .map(|value| FieldElement { value }),
725                    Add => {
726                        let value = modular_arithmetic::add(lhv, rhv, p);
727                        Some(FieldElement { value })
728                    }
729                    Sub => {
730                        let value = modular_arithmetic::sub(lhv, rhv, p);
731                        Some(FieldElement { value })
732                    }
733                    Pow => {
734                        let value = modular_arithmetic::pow(lhv, rhv, p);
735                        Some(FieldElement { value })
736                    }
737                    IntDiv => modular_arithmetic::idiv(lhv, rhv, p)
738                        .ok()
739                        .map(|value| FieldElement { value }),
740                    Mod => modular_arithmetic::mod_op(lhv, rhv, p)
741                        .ok()
742                        .map(|value| FieldElement { value }),
743                    ShiftL => modular_arithmetic::shift_l(lhv, rhv, p)
744                        .ok()
745                        .map(|value| FieldElement { value }),
746                    ShiftR => modular_arithmetic::shift_r(lhv, rhv, p)
747                        .ok()
748                        .map(|value| FieldElement { value }),
749                    LesserEq => {
750                        let value = modular_arithmetic::lesser_eq(lhv, rhv, p);
751                        Some(Boolean { value: modular_arithmetic::as_bool(&value, p) })
752                    }
753                    GreaterEq => {
754                        let value = modular_arithmetic::greater_eq(lhv, rhv, p);
755                        Some(Boolean { value: modular_arithmetic::as_bool(&value, p) })
756                    }
757                    Lesser => {
758                        let value = modular_arithmetic::lesser(lhv, rhv, p);
759                        Some(Boolean { value: modular_arithmetic::as_bool(&value, p) })
760                    }
761                    Greater => {
762                        let value = modular_arithmetic::greater(lhv, rhv, p);
763                        Some(Boolean { value: modular_arithmetic::as_bool(&value, p) })
764                    }
765                    Eq => {
766                        let value = modular_arithmetic::eq(lhv, rhv, p);
767                        Some(Boolean { value: modular_arithmetic::as_bool(&value, p) })
768                    }
769                    NotEq => {
770                        let value = modular_arithmetic::not_eq(lhv, rhv, p);
771                        Some(Boolean { value: modular_arithmetic::as_bool(&value, p) })
772                    }
773                    BitOr => {
774                        let value = modular_arithmetic::bit_or(lhv, rhv, p);
775                        Some(FieldElement { value })
776                    }
777                    BitAnd => {
778                        let value = modular_arithmetic::bit_and(lhv, rhv, p);
779                        Some(FieldElement { value })
780                    }
781                    BitXor => {
782                        let value = modular_arithmetic::bit_xor(lhv, rhv, p);
783                        Some(FieldElement { value })
784                    }
785                    // Remaining operations do not make sense.
786                    // TODO: Add report/error propagation here.
787                    _ => None,
788                }
789            }
790            // lhv and rhv reduce to two booleans.
791            (Some(Boolean { value: lhv }), Some(Boolean { value: rhv })) => {
792                use ExpressionInfixOpcode::*;
793                match self {
794                    BoolAnd => Some(Boolean { value: *lhv && *rhv }),
795                    BoolOr => Some(Boolean { value: *lhv || *rhv }),
796                    // Remaining operations do not make sense.
797                    // TODO: Add report propagation here as well.
798                    _ => None,
799                }
800            }
801            _ => None,
802        }
803    }
804}
805
806impl ExpressionPrefixOpcode {
807    fn propagate_degrees(&self, range: Option<&DegreeRange>) -> Option<DegreeRange> {
808        if let Some(range) = range {
809            use ExpressionPrefixOpcode::*;
810            match self {
811                Sub => Some(range.prefix_sub()),
812                Complement => Some(range.complement()),
813                BoolNot => Some(range.bool_not()),
814            }
815        } else {
816            None
817        }
818    }
819
820    fn propagate_values(
821        &self,
822        rhe: Option<&ValueReduction>,
823        env: &ValueEnvironment,
824    ) -> Option<ValueReduction> {
825        let p = env.prime();
826
827        use ValueReduction::*;
828        match rhe {
829            // arg reduces to a field element.
830            Some(FieldElement { value: arg }) => {
831                use ExpressionPrefixOpcode::*;
832                match self {
833                    Sub => {
834                        let value = modular_arithmetic::prefix_sub(arg, p);
835                        Some(FieldElement { value })
836                    }
837                    Complement => {
838                        let value = modular_arithmetic::complement_256(arg, p);
839                        Some(FieldElement { value })
840                    }
841                    // Remaining operations do not make sense.
842                    // TODO: Add report propagation here as well.
843                    _ => None,
844                }
845            }
846            // arg reduces to a boolean.
847            Some(Boolean { value: arg }) => {
848                use ExpressionPrefixOpcode::*;
849                match self {
850                    BoolNot => Some(Boolean { value: !arg }),
851                    // Remaining operations do not make sense.
852                    // TODO: Add report propagation here as well.
853                    _ => None,
854                }
855            }
856            None => None,
857        }
858    }
859}
860
861impl fmt::Debug for Expression {
862    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
863        use Expression::*;
864        match self {
865            Number(_, value) => write!(f, "{value}"),
866            Variable { name, .. } => {
867                write!(f, "{name:?}")
868            }
869            InfixOp { lhe, infix_op, rhe, .. } => write!(f, "({lhe:?} {infix_op} {rhe:?})"),
870            PrefixOp { prefix_op, rhe, .. } => write!(f, "({prefix_op}{rhe:?})"),
871            SwitchOp { cond, if_true, if_false, .. } => {
872                write!(f, "({cond:?}? {if_true:?} : {if_false:?})")
873            }
874            Call { name: id, args, .. } => write!(f, "{}({})", id, vec_to_debug(args, ", ")),
875            InlineArray { values, .. } => write!(f, "[{}]", vec_to_debug(values, ", ")),
876            Access { var, access, .. } => {
877                let access = access
878                    .iter()
879                    .map(|access| match access {
880                        AccessType::ArrayAccess(index) => format!("{index:?}"),
881                        AccessType::ComponentAccess(name) => name.clone(),
882                    })
883                    .collect::<Vec<_>>()
884                    .join(", ");
885                write!(f, "access({var:?}, [{access}])")
886            }
887            Update { var, access, rhe, .. } => {
888                let access = access
889                    .iter()
890                    .map(|access| match access {
891                        AccessType::ArrayAccess(index) => format!("{index:?}"),
892                        AccessType::ComponentAccess(name) => name.clone(),
893                    })
894                    .collect::<Vec<_>>()
895                    .join(", ");
896                write!(f, "update({var:?}, [{access}], {rhe:?})")
897            }
898            Phi { args, .. } => write!(f, "φ({})", vec_to_debug(args, ", ")),
899        }
900    }
901}
902
903impl fmt::Display for Expression {
904    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
905        use Expression::*;
906        match self {
907            Number(_, value) => write!(f, "{value}"),
908            Variable { name, .. } => {
909                write!(f, "{name}")
910            }
911            InfixOp { lhe, infix_op, rhe, .. } => write!(f, "({lhe} {infix_op} {rhe})"),
912            PrefixOp { prefix_op, rhe, .. } => write!(f, "{prefix_op}({rhe})"),
913            SwitchOp { cond, if_true, if_false, .. } => {
914                write!(f, "({cond}? {if_true} : {if_false})")
915            }
916            Call { name: id, args, .. } => write!(f, "{}({})", id, vec_to_display(args, ", ")),
917            InlineArray { values, .. } => write!(f, "[{}]", vec_to_display(values, ", ")),
918            Access { var, access, .. } => {
919                write!(f, "{var}")?;
920                for access in access {
921                    write!(f, "{access}")?;
922                }
923                Ok(())
924            }
925            Update { rhe, .. } => {
926                // `Update` nodes are handled at the statement level. If we are
927                // trying to display the RHS of an array assignment, we probably
928                // want the `rhe` input.
929                write!(f, "{rhe}")
930            }
931            Phi { args, .. } => write!(f, "φ({})", vec_to_display(args, ", ")),
932        }
933    }
934}
935
936impl fmt::Display for ExpressionInfixOpcode {
937    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
938        use ExpressionInfixOpcode::*;
939        match self {
940            Mul => f.write_str("*"),
941            Div => f.write_str("/"),
942            Add => f.write_str("+"),
943            Sub => f.write_str("-"),
944            Pow => f.write_str("**"),
945            IntDiv => f.write_str("\\"),
946            Mod => f.write_str("%"),
947            ShiftL => f.write_str("<<"),
948            ShiftR => f.write_str(">>"),
949            LesserEq => f.write_str("<="),
950            GreaterEq => f.write_str(">="),
951            Lesser => f.write_str("<"),
952            Greater => f.write_str(">"),
953            Eq => f.write_str("=="),
954            NotEq => f.write_str("!="),
955            BoolOr => f.write_str("||"),
956            BoolAnd => f.write_str("&&"),
957            BitOr => f.write_str("|"),
958            BitAnd => f.write_str("&"),
959            BitXor => f.write_str("^"),
960        }
961    }
962}
963
964impl fmt::Display for ExpressionPrefixOpcode {
965    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
966        use ExpressionPrefixOpcode::*;
967        match self {
968            Sub => f.write_str("-"),
969            BoolNot => f.write_str("!"),
970            Complement => f.write_str("~"),
971        }
972    }
973}
974
975impl fmt::Debug for AccessType {
976    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
977        use AccessType::*;
978        match self {
979            ArrayAccess(index) => write!(f, "{index:?}"),
980            ComponentAccess(name) => write!(f, "{name:?}"),
981        }
982    }
983}
984
985impl fmt::Display for AccessType {
986    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
987        use AccessType::*;
988        match self {
989            ArrayAccess(index) => write!(f, "[{index}]"),
990            ComponentAccess(name) => write!(f, ".{name}"),
991        }
992    }
993}
994
995#[must_use]
996fn vec_to_debug<T: fmt::Debug>(elems: &[T], sep: &str) -> String {
997    elems.iter().map(|elem| format!("{elem:?}")).collect::<Vec<String>>().join(sep)
998}
999
1000#[must_use]
1001fn vec_to_display<T: fmt::Display>(elems: &[T], sep: &str) -> String {
1002    elems.iter().map(|elem| format!("{elem}")).collect::<Vec<String>>().join(sep)
1003}
1004
1005#[cfg(test)]
1006mod tests {
1007    use crate::constants::{UsefulConstants, Curve};
1008
1009    use super::*;
1010
1011    #[test]
1012    fn test_propagate_values() {
1013        use Expression::*;
1014        use ExpressionInfixOpcode::*;
1015        use ValueReduction::*;
1016        let mut lhe = Number(Meta::default(), 7u64.into());
1017        let mut rhe = Variable { meta: Meta::default(), name: VariableName::from_string("v") };
1018        let constants = UsefulConstants::new(&Curve::default());
1019        let mut env = ValueEnvironment::new(&constants);
1020        env.add_variable(&VariableName::from_string("v"), &FieldElement { value: 3u64.into() });
1021        lhe.propagate_values(&mut env);
1022        rhe.propagate_values(&mut env);
1023
1024        // Infix multiplication.
1025        let mut expr = InfixOp {
1026            meta: Meta::default(),
1027            infix_op: Mul,
1028            lhe: Box::new(lhe.clone()),
1029            rhe: Box::new(rhe.clone()),
1030        };
1031        expr.propagate_values(&mut env.clone());
1032        assert_eq!(expr.value(), Some(&FieldElement { value: 21u64.into() }));
1033
1034        // Infix addition.
1035        let mut expr = InfixOp {
1036            meta: Meta::default(),
1037            infix_op: Add,
1038            lhe: Box::new(lhe.clone()),
1039            rhe: Box::new(rhe.clone()),
1040        };
1041        expr.propagate_values(&mut env.clone());
1042        assert_eq!(expr.value(), Some(&FieldElement { value: 10u64.into() }));
1043
1044        // Infix integer division.
1045        let mut expr = InfixOp {
1046            meta: Meta::default(),
1047            infix_op: IntDiv,
1048            lhe: Box::new(lhe.clone()),
1049            rhe: Box::new(rhe.clone()),
1050        };
1051        expr.propagate_values(&mut env.clone());
1052        assert_eq!(expr.value(), Some(&FieldElement { value: 2u64.into() }));
1053    }
1054}