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}