rtlola_hir/hir/
expression.rs

1use std::collections::{HashMap, HashSet};
2use std::fmt::Debug;
3use std::hash::{Hash, Hasher};
4use std::time::Duration;
5
6use itertools::{iproduct, Either};
7use rtlola_parser::ast::{InstanceOperation, WindowOperation};
8use rtlola_reporting::Span;
9use rust_decimal::Decimal;
10
11use super::{Parameter, WindowReference};
12use crate::hir::{AnnotatedType, Hir, Offset, SRef, StreamReference, WRef};
13use crate::modes::HirMode;
14
15/// Representation of the Id of an [Expression]
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
17pub struct ExprId(pub(crate) u32);
18
19/// Representation of an expression in the [RtLolaHir](crate::hir::RtLolaHir).
20///
21/// An expression contains its kind, its id and its position in the specification.
22#[derive(Debug, Clone, PartialEq)]
23pub struct Expression {
24    /// The kind of the expression
25    pub kind: ExpressionKind,
26    /// The [ExprId] of the expression
27    pub(crate) eid: ExprId,
28    /// The position of the expression in the specification
29    pub(crate) span: Span,
30}
31
32impl Expression {
33    /// Returns the [ExprId]] of the [Expression]
34    pub fn id(&self) -> ExprId {
35        self.eid
36    }
37
38    /// Returns the [Span] of the [Expression] identifying its position in the specification.
39    pub fn span(&self) -> Span {
40        self.span
41    }
42
43    /// Returns all streams that are synchronous accesses
44    ///
45    /// This function iterates over the [Expression] and retruns a vector of [StreamReference] identifying each stream that is synchronous accessed with its unique ID.
46    pub(crate) fn get_sync_accesses(&self) -> Vec<StreamReference> {
47        match &self.kind {
48            ExpressionKind::ArithLog(_, children)
49            | ExpressionKind::Tuple(children)
50            | ExpressionKind::Function(FnExprKind { args: children, .. }) => children
51                .iter()
52                .flat_map(|c| c.get_sync_accesses())
53                .collect(),
54            ExpressionKind::StreamAccess(target, kind, children) => match kind {
55                StreamAccessKind::Sync | StreamAccessKind::DiscreteWindow(_) => vec![*target]
56                    .into_iter()
57                    .chain(children.iter().flat_map(|c| c.get_sync_accesses()))
58                    .collect(),
59                _ => children
60                    .iter()
61                    .flat_map(|c| c.get_sync_accesses())
62                    .collect(),
63            },
64            ExpressionKind::Ite {
65                condition,
66                consequence,
67                alternative,
68            } => condition
69                .as_ref()
70                .get_sync_accesses()
71                .into_iter()
72                .chain(consequence.as_ref().get_sync_accesses())
73                .chain(alternative.as_ref().get_sync_accesses())
74                .collect(),
75            ExpressionKind::TupleAccess(child, _)
76            | ExpressionKind::Widen(WidenExprKind { expr: child, .. }) => {
77                child.as_ref().get_sync_accesses()
78            }
79            ExpressionKind::Default { expr, default } => expr
80                .as_ref()
81                .get_sync_accesses()
82                .into_iter()
83                .chain(default.as_ref().get_sync_accesses())
84                .collect(),
85            _ => vec![],
86        }
87    }
88}
89
90impl ValueEq for Expression {
91    fn value_eq(&self, other: &Self, parameter_map: &ExpressionContext) -> bool {
92        self.kind.value_eq(&other.kind, parameter_map)
93    }
94
95    fn value_eq_ignore_parameters(&self, other: &Self) -> bool {
96        self.kind.value_eq_ignore_parameters(&other.kind)
97    }
98}
99
100/// The kinds of an [Expression] of the [RtLolaHir](crate::hir::RtLolaHir).
101#[derive(Debug, Clone, PartialEq)]
102pub enum ExpressionKind {
103    /// Loading a [Constant]
104    LoadConstant(Constant),
105    /// Applying arithmetic or logic operation
106    ///
107    /// The first argument contains the operator of type [ArithLogOp], the second arguments contains the arguments of the operation, which are [Expressions](Expression). The vectors is structured as:
108    /// Unary: 1st argument -> operand
109    /// Binary: 1st argument -> lhs, 2nd argument -> rhs
110    /// n-ary: kth argument -> kth operand
111    ArithLog(ArithLogOp, Vec<Expression>),
112    /// Accessing another stream
113    ///
114    /// A stream access has the following arguments:
115    /// * the [StreamReference] of the stream that is accessed
116    /// * the [StreamAccessKind] of the stream access, e.g. an offset access
117    /// * the argmuents for parametrized stream accesses. This vector is empty if the stream that is accessed is not parametrized.
118    StreamAccess(SRef, StreamAccessKind, Vec<Expression>),
119    /// Accessing the n'th parameter of a parameterized stream
120    ///
121    /// This kind represents the access of a parameterized stream. For this, we use the folloing arguments:
122    /// * the first argument contains the [StreamReference] of the parametrized stream that is accessed
123    /// * the second argument contains the index of the parameter.
124    ParameterAccess(SRef, usize),
125    /// This kind represents the access to a lambda parameter
126    LambdaParameterAccess {
127        /// Reference to the instance aggregation using the lambda function
128        wref: WRef,
129        /// Reference to the parameter
130        pref: usize,
131    },
132    /// An if-then-else expression
133    ///
134    /// If the condition evaluates to true, the consequence is executed otherwise the alternative. All arguments are an [Expression].
135    Ite {
136        /// The condition of the if-then-else expression.
137        condition: Box<Expression>,
138        /// The consequence of the if-then-else expression.
139        consequence: Box<Expression>,
140        /// The alternative of the if-then-else expression.
141        alternative: Box<Expression>,
142    },
143    /// A tuple expression.
144    Tuple(Vec<Expression>),
145    /// Represents an access to a tuple element
146    ///
147    /// The second argument indicates the index of the accessed element, while the first produces the accessed tuple.
148    TupleAccess(Box<Expression>, usize),
149    /// A function call with its monomorphic type
150    Function(FnExprKind),
151    /// A function call to widen the type of an [Expression]
152    Widen(WidenExprKind),
153    /// Represents the transformation of an optional value into a "normal" one
154    Default {
155        /// The expression that results in an optional value
156        expr: Box<Expression>,
157        /// An infallible expression providing a default value of `expr` evaluates to `None`
158        default: Box<Expression>,
159    },
160}
161
162/// Representation of an function call
163//
164/// The struction contains all information for a function call in the [ExpressionKind] enum.
165#[derive(Debug, Clone, PartialEq)]
166pub struct FnExprKind {
167    /// The name of the function.
168    pub name: String,
169    /// The arguments of the function call.
170    /// Arguments never need to be coerced, @see `Expression::Convert`.
171    pub args: Vec<Expression>,
172    /// The type annoatation of the
173    pub(crate) type_param: Vec<AnnotatedType>,
174}
175
176/// Representation of the function call to widen the type of an [Expression]
177///
178/// The struction contains all information to widen an [Expression] in the [ExpressionKind] enum.
179#[derive(Debug, Clone, PartialEq)]
180pub struct WidenExprKind {
181    /// The [Expression] on which the function is called
182    pub expr: Box<Expression>,
183    /// The new type of `expr`
184    pub(crate) ty: AnnotatedType,
185}
186
187/// Represents a constant value of a certain kind.
188#[derive(Debug, Clone)]
189pub enum Literal {
190    /// String constant
191    Str(String),
192    /// Boolean constant
193    Bool(bool),
194    /// Integer constant with unknown sign
195    Integer(i64),
196    /// Integer constant known to be signed
197    SInt(i128),
198    /// Decimal constant
199    Decimal(Decimal),
200}
201
202impl Hash for Literal {
203    fn hash<H: Hasher>(&self, state: &mut H) {
204        match &self {
205            Literal::Str(str) => {
206                1.hash(state);
207                str.hash(state);
208            }
209            Literal::Bool(b) => {
210                2.hash(state);
211                b.hash(state);
212            }
213            Literal::Integer(i) => {
214                3.hash(state);
215                i.hash(state);
216            }
217            Literal::SInt(si) => {
218                4.hash(state);
219                si.hash(state);
220            }
221            Literal::Decimal(_) => {
222                5.hash(state);
223            }
224        }
225    }
226}
227
228impl PartialEq for Literal {
229    fn eq(&self, other: &Self) -> bool {
230        use self::Literal::*;
231        match (self, other) {
232            (Decimal(f1), Decimal(f2)) => f1 == f2,
233            (Decimal(_), _) | (_, Decimal(_)) => false,
234            (Str(s1), Str(s2)) => s1 == s2,
235            (Str(_), _) | (_, Str(_)) => false,
236            (Bool(b1), Bool(b2)) => b1 == b2,
237            (Bool(_), _) | (_, Bool(_)) => false,
238            (Integer(i1), Integer(i2)) => i1 == i2,
239            (Integer(_), _) | (_, Integer(_)) => false,
240            (SInt(i1), SInt(i2)) => i1 == i2,
241        }
242    }
243}
244
245impl Eq for Literal {}
246
247/// Represents a constant in the [ExpressionKind] enum of the [RtLolaHir](crate::hir::RtLolaHir).
248///
249/// The [RtLolaHir](crate::hir::RtLolaHir) differentiates between two types of constants:
250/// * Constant expressions that are declared with a name and a [Type](rtlola_parser::ast::Type), which are inline in [crate::from_ast]
251/// * Constant expressions occurring in an stream expression
252///
253/// Example:
254/// constant a: Int8 := 5
255/// output out := a    +    5
256///               ^         ^
257///               |         |
258///            inlined    basic
259#[derive(Debug, PartialEq, Clone, Eq, Hash)]
260pub enum Constant {
261    /// Basic constants occurring in stream expressions
262    Basic(Literal),
263    /// Inlined values of constant streams that are declared in the specification
264    Inlined(Inlined),
265}
266
267/// Represents inlined constant values from constant streams
268#[derive(Debug, PartialEq, Clone, Eq, Hash)]
269pub struct Inlined {
270    /// The value of the constant
271    pub lit: Literal,
272    /// The type of the constant
273    pub(crate) ty: AnnotatedType,
274}
275/// Representation of the different stream accesses
276#[derive(Debug, PartialEq, Clone, Copy, Hash, Eq)]
277pub enum StreamAccessKind {
278    /// Represents the synchronous access
279    Sync,
280    /// Represents the access to a (discrete window)[DiscreteAggr]
281    ///
282    /// The argument contains the reference to the (discrete window)[DiscreteAggr] whose value is used in the [Expression].
283    DiscreteWindow(WRef),
284    /// Represents the access to a (sliding window)[SlidingAggr]
285    ///
286    /// The argument contains the reference to the (sliding window)[SlidingAggr] whose value is used in the [Expression].
287    SlidingWindow(WRef),
288    /// Represents the access to a instance aggregation.
289    ///
290    /// The argument contains the reference to the (instance aggregation)[InstanceAggregation] whose value is used in the [Expression].
291    InstanceAggregation(WRef),
292    /// Representation of sample and hold accesses
293    Hold,
294    /// Representation of offset accesses
295    ///
296    /// The argument contains the [Offset] of the stream access.
297    Offset(Offset),
298    /// Represents the optional `get` access.
299    Get,
300    /// Represents the update check of a stream, if the target received a new value at this timestamp.
301    Fresh,
302}
303
304/// Contains all arithmetical and logical operations.
305#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
306pub enum ArithLogOp {
307    /// The `!` operator for logical inversion
308    Not,
309    /// The `-` operator for negation
310    Neg,
311    /// The `+` operator (addition)
312    Add,
313    /// The `-` operator (subtraction)
314    Sub,
315    /// The `*` operator (multiplication)
316    Mul,
317    /// The `/` operator (division)
318    Div,
319    /// The `%` operator (modulus)
320    Rem,
321    /// The `**` operator (power)
322    Pow,
323    /// The `&&` operator (logical and)
324    And,
325    /// The `||` operator (logical or)
326    Or,
327    /// The `^` operator (bitwise xor)
328    BitXor,
329    /// The `&` operator (bitwise and)
330    BitAnd,
331    /// The `|` operator (bitwise or)
332    BitOr,
333    /// The `~` operator for one's complement
334    BitNot,
335    /// The `<<` operator (shift left)
336    Shl,
337    /// The `>>` operator (shift right)
338    Shr,
339    /// The `==` operator (equality)
340    Eq,
341    /// The `<` operator (less than)
342    Lt,
343    /// The `<=` operator (less than or equal to)
344    Le,
345    /// The `!=` operator (not equal to)
346    Ne,
347    /// The `>=` operator (greater than or equal to)
348    Ge,
349    /// The `>` operator (greater than)
350    Gt,
351}
352
353/// Functionality of [sliding window](SlidingAggr) and [discrete window](DiscreteAggr) aggregations
354pub trait WindowAggregation: Debug + Copy {
355    /// Returns wheter or not the first aggregated value will be produced immediately or wheter the window waits
356    ///
357    /// The function returns `true` if the windows waits until the [Duration] has passed at least once. Otherwise the function returns `false`.
358    fn wait_until_full(&self) -> bool;
359    /// Returns the [WindowOperation] of the sliding or discrete window
360    fn operation(&self) -> WindowOperation;
361    /// Returns the duration of the window
362    ///
363    /// The function returns the duration of a [sliding window](SlidingAggr) or the number of values used for a [discrete window](DiscreteAggr).
364    fn duration(&self) -> Either<Duration, usize>;
365}
366
367/// Represents an instance of an instance aggregation
368#[derive(Debug, Clone, PartialEq)]
369pub struct InstanceAggregation {
370    /// The stream whose values will be aggregated
371    pub target: SRef,
372    /// The stream calling and evaluating this window
373    pub caller: SRef,
374    /// A filter over the instances
375    pub selection: InstanceSelection,
376    /// The operation to be performed over the instances
377    pub aggr: InstanceOperation,
378    /// The reference of this window.
379    pub(crate) reference: WRef,
380    /// The Id of the expression in which this window is accessed
381    ///
382    /// This field contains the Id of the expression that uses the produced value. It is NOT the id of the window.
383    pub(crate) eid: ExprId,
384}
385
386#[derive(Debug, Clone, PartialEq)]
387/// Enum to indicate which instances are part of the aggregation
388pub enum InstanceSelection {
389    /// Only instances that are updated in this evaluation cycle are part of the aggregation
390    Fresh,
391    /// All instances are part of the aggregation
392    All,
393    /// Only instances that are updated in this evaluation cycle and satisfy the condition are part of the aggregation
394    FilteredFresh {
395        /// The parameters of the lambda expression
396        parameters: Vec<Parameter>,
397        /// The condition that needs to be satisfied
398        cond: Box<Expression>,
399    },
400    /// All instances that satisfy the condition are part of the aggregation
401    FilteredAll {
402        /// The parameters of the lambda expression
403        parameters: Vec<Parameter>,
404        /// The condition that needs to be satisfied
405        cond: Box<Expression>,
406    },
407}
408
409impl InstanceSelection {
410    pub(crate) fn condition(&self) -> Option<&Expression> {
411        match self {
412            InstanceSelection::Fresh | InstanceSelection::All => None,
413            InstanceSelection::FilteredFresh {
414                parameters: _,
415                cond,
416            }
417            | InstanceSelection::FilteredAll {
418                parameters: _,
419                cond,
420            } => Some(cond),
421        }
422    }
423}
424
425/// Combines the functionality of Instance and Window Aggregations
426pub trait Aggregation {
427    /// Returns the reference of the window
428    fn reference(&self) -> WindowReference;
429
430    /// Returns the Id of the expression in which this window is accessed
431    ///
432    /// The return value contains the Id of the expression that uses the produced value. This value is NOT the id of the window.
433    fn id(&self) -> ExprId;
434}
435
436impl Aggregation for InstanceAggregation {
437    fn reference(&self) -> WindowReference {
438        self.reference
439    }
440
441    fn id(&self) -> ExprId {
442        self.eid
443    }
444}
445
446/// Represents a sliding window aggregation
447///
448/// The struct contains all information that is specific for a sliding window aggregation. The data that is shared between a sliding window aggregation and a discrete window aggregation is stored a [Window].
449#[derive(Clone, Debug, Copy, PartialEq, Eq)]
450pub struct SlidingAggr {
451    /// Flag to indicate whether or not the first aggregated value will be produced immediately or whether the window waits until `duration` has passed at least once.
452    pub wait: bool,
453    /// The aggregation operation
454    pub op: WindowOperation,
455    /// The duration of the window
456    ///
457    /// The duration of a sliding window is a time span.
458    pub duration: Duration,
459}
460
461impl WindowAggregation for SlidingAggr {
462    fn wait_until_full(&self) -> bool {
463        self.wait
464    }
465
466    fn operation(&self) -> WindowOperation {
467        self.op
468    }
469
470    fn duration(&self) -> Either<Duration, usize> {
471        Either::Left(self.duration)
472    }
473}
474/// Represents a discrete window aggregation
475///
476/// The struct contains all information that is specific for a discrete window aggregation. The data that is shared between a sliding window aggregation and a discrete window aggregation is stored a [Window].
477
478#[derive(Clone, Debug, Copy, PartialEq, Eq)]
479pub struct DiscreteAggr {
480    /// Flag to indicate whether or not the first aggregated value will be produced immediately or whether the window waits until `duration` has passed at least once.
481    pub wait: bool,
482    /// The aggregation operation
483    pub op: WindowOperation,
484    /// The duration of the window
485    ///
486    /// The duration of a discrete window is a discrete number of values.
487    pub duration: usize,
488}
489
490impl WindowAggregation for DiscreteAggr {
491    fn wait_until_full(&self) -> bool {
492        self.wait
493    }
494
495    fn operation(&self) -> WindowOperation {
496        self.op
497    }
498
499    fn duration(&self) -> Either<Duration, usize> {
500        Either::Right(self.duration)
501    }
502}
503
504/// Represents an instance of a sliding or a discrete window aggregation
505///
506/// The generatic `Aggr` defines if the instance is a slinding window or a discrete window.
507/// The field `aggr` contains the data that is specific for a discrete of sliding window.
508/// The other data is used for a discrete and a sliding window.
509#[derive(Debug, PartialEq, Eq, Clone, Copy)]
510pub struct Window<Aggr: WindowAggregation> {
511    /// The stream whose values will be aggregated
512    pub target: SRef,
513    /// The stream calling and evaluating this window
514    pub caller: SRef,
515    /// The data that differentiates a sliding and a discrete window
516    ///
517    /// This field can either has the type [SlidingAggr] or [DiscreteAggr].
518    pub aggr: Aggr,
519    /// The reference of this window.
520    pub(crate) reference: WRef,
521    /// The Id of the expression in which this window is accessed
522    ///
523    /// This field contains the Id of the expression that uses the produced value. It is NOT the id of the window.
524    pub(crate) eid: ExprId,
525}
526
527impl<A: WindowAggregation> Aggregation for Window<A> {
528    /// Returns the reference of the window
529    fn reference(&self) -> WindowReference {
530        self.reference
531    }
532
533    /// Returns the Id of the expression in which this window is accessed
534    ///
535    /// The return value contains the Id of the expression that uses the produced value. This value is NOT the id of the window.
536    fn id(&self) -> ExprId {
537        self.eid
538    }
539}
540
541/// A context for expressions that establishes equality between parameters of different streams based on their spawn expression.
542// Maps a stream 'a' and a parameter 'x' of stream 'b' to the set of matching parameters of 'a'
543#[derive(Debug, Clone)]
544pub(crate) struct ExpressionContext(HashMap<SRef, HashMap<(SRef, usize), HashSet<usize>>>);
545
546impl ExpressionContext {
547    // Two parameters of two streams are equal if they are spawned with the same expression under the same condition.
548    // For example:
549    //
550    // output a(x, y) spawn with (5, 42) if i = 2 ...
551    // output b(v, w) spawn with (42, 7) if i = 2 ...
552    //
553    // Then parameter y of a is equal to the parameter v of b
554    // The spawn condition has to match as otherwise y could already be initialized while v is not.
555    //
556    // This equivalence is computed as follows:
557    //
558    // For all pairs of output streams that have parameters and the same spawn condition:
559    //      Get the vector of expressions that initialize the parameters of a (i.e. (5, 42))
560    //      Get the vector of expressions that initialize the parameters of b (i.e. (42, 7))
561    //      For all paris of expressions in the cross product of these two vectors (i.e. (5, 42), (5, 7), (42, 42), (42, 7)):
562    //          check if the two expressions are equal
563    //              if true then insert the corresponding parameter into the resulting set
564    pub(crate) fn new<M: HirMode>(hir: &Hir<M>) -> ExpressionContext {
565        let mut inner = HashMap::with_capacity(hir.outputs.len());
566        for current in hir.outputs() {
567            let mut para_mapping: HashMap<(SRef, usize), HashSet<usize>> = HashMap::new();
568
569            let cur_spawn_cond = current.spawn().and_then(|st| st.spawn_cond(hir));
570
571            let current_spawn_args = current
572                .spawn()
573                .map(|st| st.spawn_args(hir))
574                .unwrap_or_default();
575
576            assert_eq!(current.params.len(), current_spawn_args.len());
577
578            if !current.params.is_empty() {
579                for target in hir.outputs() {
580                    // if both have a spawn condition they must match
581                    let target_spawn_cond = target.spawn().and_then(|st| st.spawn_cond(hir));
582                    let cond_match = match (cur_spawn_cond, target_spawn_cond) {
583                        (Some(e1), Some(e2)) => e1.value_eq_ignore_parameters(e2),
584                        (None, None) => true,
585                        _ => false,
586                    };
587                    if !target.params.is_empty() && cond_match {
588                        let target_spawn_args = target
589                            .spawn()
590                            .map(|st| st.spawn_args(hir))
591                            .unwrap_or_default();
592
593                        assert_eq!(target.params.len(), target_spawn_args.len());
594
595                        iproduct!(
596                            current_spawn_args.iter().enumerate(),
597                            target_spawn_args.iter().enumerate()
598                        )
599                        .filter_map(|((current_para, current_exp), (target_para, target_exp))| {
600                            if current_exp.value_eq_ignore_parameters(target_exp) {
601                                Some(((target.sr, target_para), current_para))
602                            } else {
603                                None
604                            }
605                        })
606                        .for_each(|(k, v)| {
607                            if let Some(paras) = para_mapping.get_mut(&k) {
608                                paras.insert(v);
609                            } else {
610                                para_mapping
611                                    .insert(k, vec![v].into_iter().collect::<HashSet<usize>>());
612                            }
613                        });
614                    }
615                }
616            }
617
618            inner.insert(current.sr, para_mapping);
619        }
620        ExpressionContext(inner)
621    }
622
623    /// Checks if the parameter of source matches the parameter of target
624    pub(crate) fn matches(
625        &self,
626        source: SRef,
627        source_parameter: usize,
628        target: SRef,
629        target_parameter: usize,
630    ) -> bool {
631        self.0
632            .get(&source)
633            .and_then(|para_map| para_map.get(&(target, target_parameter)))
634            .map(|para_set| para_set.contains(&source_parameter))
635            .unwrap_or(false)
636    }
637
638    #[cfg(test)]
639    /// Extracts the parameter mapping for a single stream from the context
640    /// Query the map for a stream b with a parameter p to get the parameter q of the stream if it matches with parameter p
641    pub(crate) fn map_for(&self, stream: SRef) -> &HashMap<(SRef, usize), HashSet<usize>> {
642        self.0
643            .get(&stream)
644            .expect("Invalid initialization of ExpressionContext")
645    }
646}
647
648pub(crate) trait ValueEq {
649    fn value_eq(&self, other: &Self, parameter_map: &ExpressionContext) -> bool;
650    #[cfg(test)]
651    fn value_neq(&self, other: &Self, parameter_map: &ExpressionContext) -> bool {
652        !self.value_eq(other, parameter_map)
653    }
654
655    fn value_eq_ignore_parameters(&self, other: &Self) -> bool;
656    #[cfg(test)]
657    fn value_neq_ignore_parameters(&self, other: &Self) -> bool {
658        !self.value_eq_ignore_parameters(other)
659    }
660}
661
662impl ValueEq for ExpressionKind {
663    fn value_eq(&self, other: &Self, parameter_map: &ExpressionContext) -> bool {
664        use self::ExpressionKind::*;
665        match (self, other) {
666            (ParameterAccess(sref, idx), ParameterAccess(sref2, idx2)) => {
667                parameter_map.matches(*sref, *idx, *sref2, *idx2)
668            }
669            (LoadConstant(c1), LoadConstant(c2)) => c1 == c2,
670            (ArithLog(op, args), ArithLog(op2, args2)) => {
671                op == op2
672                    && args.len() == args2.len()
673                    && args
674                        .iter()
675                        .zip(args2.iter())
676                        .all(|(a1, a2)| a1.value_eq(a2, parameter_map))
677            }
678            (StreamAccess(sref, kind, args), StreamAccess(sref2, kind2, args2)) => {
679                sref == sref2
680                    && kind == kind2
681                    && args.len() == args2.len()
682                    && args
683                        .iter()
684                        .zip(args2.iter())
685                        .all(|(a1, a2)| a1.value_eq(a2, parameter_map))
686            }
687            (
688                Ite {
689                    condition: c1,
690                    consequence: c2,
691                    alternative: c3,
692                },
693                Ite {
694                    condition: b1,
695                    consequence: b2,
696                    alternative: b3,
697                },
698            ) => {
699                c1.value_eq(b1, parameter_map)
700                    && c2.value_eq(b2, parameter_map)
701                    && c3.value_eq(b3, parameter_map)
702            }
703            (Tuple(args), Tuple(args2)) => {
704                args.len() == args2.len()
705                    && args
706                        .iter()
707                        .zip(args2.iter())
708                        .all(|(a1, a2)| a1.value_eq(a2, parameter_map))
709            }
710            (TupleAccess(inner, i1), TupleAccess(inner2, i2)) => {
711                i1 == i2 && inner.value_eq(inner2, parameter_map)
712            }
713            (
714                Function(FnExprKind {
715                    name,
716                    args,
717                    type_param,
718                }),
719                Function(FnExprKind {
720                    name: name2,
721                    args: args2,
722                    type_param: type_param2,
723                }),
724            ) => {
725                name == name2
726                    && type_param == type_param2
727                    && args.len() == args2.len()
728                    && args
729                        .iter()
730                        .zip(args2.iter())
731                        .all(|(a1, a2)| a1.value_eq(a2, parameter_map))
732            }
733            (
734                Widen(WidenExprKind {
735                    expr: inner,
736                    ty: t1,
737                }),
738                Widen(WidenExprKind {
739                    expr: inner2,
740                    ty: t2,
741                }),
742            ) => t1 == t2 && inner.value_eq(inner2, parameter_map),
743            (
744                Default { expr, default },
745                Default {
746                    expr: expr2,
747                    default: default2,
748                },
749            ) => expr.value_eq(expr2, parameter_map) && default.value_eq(default2, parameter_map),
750            _ => false,
751        }
752    }
753
754    fn value_eq_ignore_parameters(&self, other: &Self) -> bool {
755        use ExpressionKind::*;
756        match (self, other) {
757            (ParameterAccess(sref, idx), ParameterAccess(sref2, idx2)) => {
758                sref == sref2 && idx == idx2
759            }
760            (LoadConstant(c1), LoadConstant(c2)) => c1 == c2,
761            (ArithLog(op, args), ArithLog(op2, args2)) => {
762                op == op2
763                    && args.len() == args2.len()
764                    && args
765                        .iter()
766                        .zip(args2.iter())
767                        .all(|(a1, a2)| a1.value_eq_ignore_parameters(a2))
768            }
769            (StreamAccess(sref, kind, args), StreamAccess(sref2, kind2, args2)) => {
770                sref == sref2
771                    && kind == kind2
772                    && args.len() == args2.len()
773                    && args
774                        .iter()
775                        .zip(args2.iter())
776                        .all(|(a1, a2)| a1.value_eq_ignore_parameters(a2))
777            }
778            (
779                Ite {
780                    condition: c1,
781                    consequence: c2,
782                    alternative: c3,
783                },
784                Ite {
785                    condition: b1,
786                    consequence: b2,
787                    alternative: b3,
788                },
789            ) => {
790                c1.value_eq_ignore_parameters(b1)
791                    && c2.value_eq_ignore_parameters(b2)
792                    && c3.value_eq_ignore_parameters(b3)
793            }
794            (Tuple(args), Tuple(args2)) => {
795                args.len() == args2.len()
796                    && args
797                        .iter()
798                        .zip(args2.iter())
799                        .all(|(a1, a2)| a1.value_eq_ignore_parameters(a2))
800            }
801            (TupleAccess(inner, i1), TupleAccess(inner2, i2)) => {
802                i1 == i2 && inner.value_eq_ignore_parameters(inner2)
803            }
804            (
805                Function(FnExprKind {
806                    name,
807                    args,
808                    type_param,
809                }),
810                Function(FnExprKind {
811                    name: name2,
812                    args: args2,
813                    type_param: type_param2,
814                }),
815            ) => {
816                name == name2
817                    && type_param == type_param2
818                    && args.len() == args2.len()
819                    && args
820                        .iter()
821                        .zip(args2.iter())
822                        .all(|(a1, a2)| a1.value_eq_ignore_parameters(a2))
823            }
824            (
825                Widen(WidenExprKind {
826                    expr: inner,
827                    ty: t1,
828                }),
829                Widen(WidenExprKind {
830                    expr: inner2,
831                    ty: t2,
832                }),
833            ) => t1 == t2 && inner.value_eq_ignore_parameters(inner2),
834            (
835                Default { expr, default },
836                Default {
837                    expr: expr2,
838                    default: default2,
839                },
840            ) => {
841                expr.value_eq_ignore_parameters(expr2)
842                    && default.value_eq_ignore_parameters(default2)
843            }
844            _ => false,
845        }
846    }
847}