Skip to main content

veryl_analyzer/ir/
statement.rs

1use crate::AnalyzerError;
2use crate::conv::Context;
3use crate::ir::assign_table::{AssignContext, AssignTable};
4use crate::ir::utils::{allow_missing_reset_statement, has_cond_type};
5use crate::ir::{
6    Comptime, Expression, FfTable, FunctionCall, Op, SystemFunctionCall, Type, VarId, VarIndex,
7    VarPath, VarSelect,
8};
9use crate::value::{Value, ValueBigUint};
10use indent::indent_all_by;
11use std::borrow::Cow;
12use std::fmt;
13use veryl_parser::resource_table::StrId;
14use veryl_parser::token_range::TokenRange;
15
16#[derive(Clone, Default)]
17pub struct StatementBlock(pub Vec<Statement>);
18
19#[derive(Clone, Copy, PartialEq, Eq)]
20pub enum ControlFlow {
21    Continue,
22    Break,
23}
24
25#[derive(Clone)]
26pub enum Statement {
27    Assign(AssignStatement),
28    If(IfStatement),
29    IfReset(IfResetStatement),
30    For(ForStatement),
31    SystemFunctionCall(Box<SystemFunctionCall>),
32    FunctionCall(Box<FunctionCall>),
33    TbMethodCall(TbMethodCall),
34    Break,
35    Unsupported(TokenRange),
36    Null,
37}
38
39#[derive(Clone)]
40pub struct ForStatement {
41    pub var_id: VarId,
42    pub var_name: StrId,
43    pub var_type: Type,
44    pub range: ForRange,
45    pub body: Vec<Statement>,
46    pub token: TokenRange,
47}
48
49#[derive(Clone, Debug)]
50pub enum ForBound {
51    Const(usize),
52    Expression(Box<Expression>),
53}
54
55impl ForBound {
56    pub fn eval_value(&self, context: &mut Context) -> Option<usize> {
57        match self {
58            Self::Const(x) => Some(*x),
59            Self::Expression(exp) => {
60                let exp = exp.as_ref().clone();
61                exp.eval_value(context)?.to_usize()
62            }
63        }
64    }
65}
66
67impl fmt::Display for ForBound {
68    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69        match self {
70            ForBound::Const(x) => x.fmt(f),
71            ForBound::Expression(x) => write!(f, "{}", x.as_ref()),
72        }
73    }
74}
75
76/// Loop iteration range representation.
77#[derive(Clone, Debug)]
78pub enum ForRange {
79    /// start..end with additive step (default step=1)
80    Forward {
81        start: ForBound,
82        end: ForBound,
83        inclusive: bool,
84        step: usize,
85    },
86    /// (start..end).rev() with additive step (default step=1)
87    Reverse {
88        start: ForBound,
89        end: ForBound,
90        inclusive: bool,
91        step: usize,
92    },
93    /// start..end with arbitrary step operator (e.g., step *= 2)
94    Stepped {
95        start: ForBound,
96        end: ForBound,
97        inclusive: bool,
98        step: usize,
99        op: Op,
100    },
101}
102
103impl ForRange {
104    pub fn eval_iter(&self, context: &mut Context) -> Option<Vec<usize>> {
105        let limit = context.config.evaluate_size_limit;
106        match self {
107            ForRange::Forward {
108                start,
109                end,
110                inclusive,
111                step,
112            } => {
113                let start = start.eval_value(context)?;
114                let end = end.eval_value(context)?;
115                let end = if *inclusive { end + 1 } else { end };
116                if end.saturating_sub(start) > limit {
117                    return None;
118                }
119                if *step == 1 {
120                    Some((start..end).collect())
121                } else {
122                    let mut ret = vec![];
123                    let mut i = start;
124                    while i < end {
125                        ret.push(i);
126                        i += step;
127                    }
128                    Some(ret)
129                }
130            }
131            ForRange::Reverse {
132                start,
133                end,
134                inclusive,
135                ..
136            } => {
137                let start = start.eval_value(context)?;
138                let end = end.eval_value(context)?;
139                if end.saturating_sub(start) > limit {
140                    return None;
141                }
142                if *inclusive {
143                    Some((start..=end).rev().collect())
144                } else {
145                    Some((start..end).rev().collect())
146                }
147            }
148            ForRange::Stepped {
149                start,
150                end,
151                inclusive,
152                step,
153                op,
154            } => {
155                let start = start.eval_value(context)?;
156                let end = end.eval_value(context)?;
157                let end = if *inclusive { end + 1 } else { end };
158                let mut ret = vec![];
159                let mut i = start;
160                while i < end {
161                    if ret.len() > limit {
162                        return None;
163                    }
164                    ret.push(i);
165                    let new_i = op.eval(i, *step);
166                    if new_i == i {
167                        break;
168                    }
169                    i = new_i;
170                }
171                Some(ret)
172            }
173        }
174    }
175}
176
177#[derive(Clone)]
178pub struct TbMethodCall {
179    pub inst: StrId,
180    pub method: TbMethod,
181}
182
183#[derive(Clone)]
184pub enum TbMethod {
185    ClockNext {
186        count: Option<Expression>,
187        period: Option<Expression>,
188    },
189    ResetAssert {
190        clock: StrId,
191        duration: Option<Expression>,
192    },
193}
194
195impl Statement {
196    pub fn is_null(&self) -> bool {
197        matches!(self, Statement::Null)
198    }
199
200    pub fn eval_value(&self, context: &mut Context) -> ControlFlow {
201        match self {
202            Statement::Assign(x) => {
203                x.eval_value(context);
204                ControlFlow::Continue
205            }
206            Statement::If(x) => x.eval_value(context),
207            Statement::IfReset(_) => ControlFlow::Continue,
208            Statement::For(x) => {
209                if let Some(iter) = x.range.eval_iter(context) {
210                    'outer: for i in iter {
211                        if let Some(var) = context.variables.get_mut(&x.var_id)
212                            && let Some(total_width) = x.var_type.total_width()
213                        {
214                            let val = Value::new(i as u64, total_width, x.var_type.signed);
215                            var.set_value(&[], val, None);
216                        }
217                        for s in &x.body {
218                            if s.eval_value(context) == ControlFlow::Break {
219                                break 'outer;
220                            }
221                        }
222                    }
223                }
224                ControlFlow::Continue
225            }
226            Statement::SystemFunctionCall(_) => ControlFlow::Continue,
227            Statement::FunctionCall(x) => {
228                x.eval_value(context);
229                ControlFlow::Continue
230            }
231            Statement::TbMethodCall(_) => ControlFlow::Continue,
232            Statement::Break => ControlFlow::Break,
233            Statement::Unsupported(_) => ControlFlow::Continue,
234            Statement::Null => ControlFlow::Continue,
235        }
236    }
237
238    pub fn eval_assign(
239        &self,
240        context: &mut Context,
241        assign_table: &mut AssignTable,
242        assign_context: AssignContext,
243        base_tables: &[&AssignTable],
244    ) {
245        match self {
246            Statement::Assign(x) => x.eval_assign(context, assign_table, assign_context),
247            Statement::If(x) => x.eval_assign(context, assign_table, assign_context, base_tables),
248            Statement::IfReset(x) => x.eval_assign(context, assign_table, base_tables),
249            Statement::SystemFunctionCall(x) => {
250                x.eval_assign(context, assign_table, assign_context)
251            }
252            Statement::FunctionCall(x) => x.eval_assign(context, assign_table, assign_context),
253            Statement::For(x) => {
254                for s in &x.body {
255                    s.eval_assign(context, assign_table, assign_context, base_tables);
256                }
257            }
258            Statement::TbMethodCall(_) | Statement::Break => (),
259            Statement::Unsupported(_) => (),
260            Statement::Null => (),
261        }
262    }
263
264    pub fn gather_ff(&self, context: &mut Context, table: &mut FfTable, decl: usize) {
265        match self {
266            Statement::Assign(x) => x.gather_ff(context, table, decl),
267            Statement::If(x) => x.gather_ff(context, table, decl),
268            Statement::IfReset(x) => x.gather_ff(context, table, decl),
269            Statement::FunctionCall(x) => x.gather_ff(context, table, decl, None, true),
270            Statement::SystemFunctionCall(x) => x.gather_ff(context, table, decl, true),
271            Statement::For(x) => {
272                for s in &x.body {
273                    s.gather_ff(context, table, decl);
274                }
275            }
276            Statement::TbMethodCall(_)
277            | Statement::Break
278            | Statement::Unsupported(_)
279            | Statement::Null => (),
280        }
281    }
282
283    pub fn gather_ff_comb_assign(&self, context: &mut Context, table: &mut FfTable, decl: usize) {
284        match self {
285            Statement::Assign(x) => x.gather_ff_comb_assign(context, table, decl),
286            Statement::If(x) => {
287                // Record the condition expression's reads so analyses
288                // that distinguish comb vs ff context (e.g. comb-to-FF
289                // hoist eligibility) see them.  `from_ff=false` keeps
290                // these out of `is_ff` classification.
291                x.cond.gather_ff(context, table, decl, None, false);
292                for s in &x.true_side {
293                    s.gather_ff_comb_assign(context, table, decl);
294                }
295                for s in &x.false_side {
296                    s.gather_ff_comb_assign(context, table, decl);
297                }
298            }
299            Statement::IfReset(x) => {
300                for s in &x.true_side {
301                    s.gather_ff_comb_assign(context, table, decl);
302                }
303                for s in &x.false_side {
304                    s.gather_ff_comb_assign(context, table, decl);
305                }
306            }
307            Statement::FunctionCall(x) => x.gather_ff_comb_assign(context, table, decl),
308            Statement::SystemFunctionCall(x) => x.gather_ff(context, table, decl, false),
309            Statement::For(x) => {
310                for s in &x.body {
311                    s.gather_ff_comb_assign(context, table, decl);
312                }
313            }
314            _ => (),
315        }
316    }
317
318    pub fn set_index(&mut self, index: &VarIndex) {
319        match self {
320            Statement::Assign(x) => x.set_index(index),
321            Statement::If(x) => x.set_index(index),
322            Statement::IfReset(x) => x.set_index(index),
323            Statement::SystemFunctionCall(_) => (),
324            Statement::FunctionCall(x) => x.set_index(index),
325            Statement::For(x) => {
326                for s in &mut x.body {
327                    s.set_index(index);
328                }
329            }
330            Statement::TbMethodCall(_) | Statement::Break => (),
331            Statement::Unsupported(_) => (),
332            Statement::Null => (),
333        }
334    }
335}
336
337impl fmt::Display for Statement {
338    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
339        match self {
340            Statement::Assign(x) => x.fmt(f),
341            Statement::If(x) => x.fmt(f),
342            Statement::IfReset(x) => x.fmt(f),
343            Statement::SystemFunctionCall(x) => format!("{x};").fmt(f),
344            Statement::FunctionCall(x) => format!("{x};").fmt(f),
345            Statement::TbMethodCall(x) => match &x.method {
346                TbMethod::ClockNext { count, .. } => {
347                    if let Some(c) = count {
348                        write!(f, "{}.next({c});", x.inst)
349                    } else {
350                        write!(f, "{}.next();", x.inst)
351                    }
352                }
353                TbMethod::ResetAssert { clock, duration } => {
354                    if let Some(d) = duration {
355                        write!(f, "{}.assert({clock}, {d});", x.inst)
356                    } else {
357                        write!(f, "{}.assert({clock});", x.inst)
358                    }
359                }
360            },
361            Statement::For(x) => {
362                let range_op = if let ForRange::Reverse { .. } = &x.range {
363                    "rev "
364                } else {
365                    ""
366                };
367                let (start, end, inclusive, step_info) = match &x.range {
368                    ForRange::Forward {
369                        start,
370                        end,
371                        inclusive,
372                        step,
373                    } => (
374                        start,
375                        end,
376                        *inclusive,
377                        if *step == 1 {
378                            None
379                        } else {
380                            Some(format!("+= {step}"))
381                        },
382                    ),
383                    ForRange::Reverse {
384                        start,
385                        end,
386                        inclusive,
387                        ..
388                    } => (start, end, *inclusive, None),
389                    ForRange::Stepped {
390                        start,
391                        end,
392                        inclusive,
393                        step,
394                        op,
395                    } => (start, end, *inclusive, Some(format!("{op}= {step}"))),
396                };
397                let dots = if inclusive { "..=" } else { ".." };
398                if let Some(step_str) = step_info {
399                    writeln!(
400                        f,
401                        "for {} in {range_op}{start}{dots}{end} step {step_str} {{",
402                        x.var_name
403                    )?;
404                } else {
405                    writeln!(f, "for {} in {range_op}{start}{dots}{end} {{", x.var_name)?;
406                }
407                for s in &x.body {
408                    writeln!(f, "  {s}")?;
409                }
410                write!(f, "}}")
411            }
412            Statement::Break => "break;".fmt(f),
413            Statement::Unsupported(_) => "/* unsupported */".fmt(f),
414            Statement::Null => "".fmt(f),
415        }
416    }
417}
418
419#[derive(Clone, Debug)]
420pub struct AssignDestination {
421    pub id: VarId,
422    pub path: VarPath,
423    pub index: VarIndex,
424    pub select: VarSelect,
425    pub comptime: Comptime,
426    pub token: TokenRange,
427}
428
429impl AssignDestination {
430    pub fn total_width(&self, context: &mut Context) -> Option<usize> {
431        let (beg, end) = self
432            .select
433            .eval_value(context, &self.comptime.r#type, false)?;
434        Some(beg - end + 1)
435    }
436
437    pub fn eval_assign(
438        &self,
439        context: &mut Context,
440        assign_table: &mut AssignTable,
441        assign_context: AssignContext,
442    ) {
443        if let Some(variable) = context.get_variable_info(self.id) {
444            let is_index_const = self.index.is_const();
445            let is_select_const = self.select.is_const();
446            let is_const = is_index_const & is_select_const;
447
448            let range = if !is_index_const {
449                variable.r#type.array.calc_range(&[])
450            } else {
451                let Some(index) = self.index.eval_value(context) else {
452                    return;
453                };
454                variable.r#type.array.calc_range(&index)
455            };
456
457            // If select is not const, assign to the whole width
458            let mask = if !is_select_const {
459                let Some(width) = variable.total_width() else {
460                    return;
461                };
462                ValueBigUint::gen_mask(width)
463            } else {
464                let Some((beg, end)) = self.select.eval_value(context, &variable.r#type, false)
465                else {
466                    return;
467                };
468                ValueBigUint::gen_mask_range(beg, end)
469            };
470
471            let mut errors = vec![];
472            if let Some((beg, end)) = range {
473                // `insert_assign` / `check_refered` bail out on arrays over
474                // `array_limit`; iterating beg..=end just to hit that guard
475                // is wasted work for any whole-array assignment.
476                let skip_large_array =
477                    variable.r#type.total_array().unwrap_or(0) > assign_table.array_limit;
478
479                if !skip_large_array {
480                    for i in beg..=end {
481                        let index = VarIndex::from_index(i, &variable.r#type.array);
482                        if let Some(index) = index.eval_value(context) {
483                            if assign_context.is_comb()
484                                && assign_table.check_refered(&variable.id, &index, &mask)
485                            {
486                                let mut text = variable.path.to_string();
487                                for i in &index {
488                                    text.push_str(&format!("[{i}]"));
489                                }
490                                // ignore `#[allow(unassign_variable)]` attribute
491                                errors.push(AnalyzerError::unassign_variable(&text, &self.token));
492                            }
493
494                            let maybe = !is_const | assign_context.is_system_verilog();
495                            let _ = assign_table.insert_assign(
496                                &variable,
497                                index,
498                                mask.clone(),
499                                maybe,
500                                self.token,
501                            );
502                        }
503                    }
504                }
505            }
506
507            for e in errors {
508                context.insert_error(e);
509            }
510        }
511    }
512
513    pub fn gather_ff(&self, context: &mut Context, table: &mut FfTable, decl: usize) {
514        if let Some(variable) = context.get_variable_info(self.id) {
515            // Let-bound variables use blocking assignment (BA) semantics
516            // and must not be registered as FF in the table.
517            if variable.kind == crate::ir::VarKind::Let {
518                return;
519            }
520            if let Some(index) = self.index.eval_value(context) {
521                if let Some(index) = variable.r#type.array.calc_index(&index) {
522                    table.insert_assigned(self.id, index, decl);
523                }
524            } else if let Some(total_array) = variable.r#type.total_array() {
525                for i in 0..total_array {
526                    table.insert_assigned(self.id, i, decl);
527                }
528            }
529        }
530    }
531
532    pub fn gather_ff_comb_assign(&self, context: &mut Context, table: &mut FfTable, decl: usize) {
533        if let Some(variable) = context.get_variable_info(self.id) {
534            if let Some(index) = self.index.eval_value(context) {
535                if let Some(index) = variable.r#type.array.calc_index(&index) {
536                    table.insert_assigned_comb(self.id, index, decl);
537                }
538            } else if let Some(total_array) = variable.r#type.total_array() {
539                for i in 0..total_array {
540                    table.insert_assigned_comb(self.id, i, decl);
541                }
542            }
543        }
544    }
545
546    pub fn set_index(&mut self, index: &VarIndex) {
547        self.index.add_prelude(index);
548    }
549}
550
551impl fmt::Display for AssignDestination {
552    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
553        let ret = format!("{}{}{}", self.id, self.index, self.select);
554        ret.fmt(f)
555    }
556}
557
558#[derive(Clone)]
559pub struct AssignStatement {
560    pub dst: Vec<AssignDestination>,
561    pub width: Option<usize>,
562    pub expr: Expression,
563    pub token: TokenRange,
564}
565
566impl AssignStatement {
567    pub fn eval_value(&self, context: &mut Context) {
568        if let Some(value) = self.expr.eval_value(context) {
569            // TODO multiple dst
570            if let Some(index) = self.dst[0].index.eval_value(context)
571                && let Some((beg, end)) =
572                    self.dst[0]
573                        .select
574                        .eval_value(context, &self.dst[0].comptime.r#type, false)
575                && let Some(variable) = context.variables.get_mut(&self.dst[0].id)
576            {
577                variable.set_value(&index, value, Some((beg, end)));
578            }
579        }
580    }
581
582    pub fn eval_assign(
583        &self,
584        context: &mut Context,
585        assign_table: &mut AssignTable,
586        assign_context: AssignContext,
587    ) {
588        self.expr.eval_assign(context, assign_table, assign_context);
589        for dst in &self.dst {
590            dst.eval_assign(context, assign_table, assign_context);
591        }
592    }
593
594    pub fn gather_ff(&self, context: &mut Context, table: &mut FfTable, decl: usize) {
595        let assign_target = self.dst.first().map(|d| {
596            let idx = d
597                .index
598                .eval_value(context)
599                .and_then(|v| context.get_variable_info(d.id)?.r#type.array.calc_index(&v));
600            (d.id, idx)
601        });
602        self.expr
603            .gather_ff(context, table, decl, assign_target, true);
604        for dst in &self.dst {
605            dst.gather_ff(context, table, decl);
606        }
607    }
608
609    pub fn gather_ff_comb_assign(&self, context: &mut Context, table: &mut FfTable, decl: usize) {
610        let assign_target = self.dst.first().map(|d| {
611            let idx = d
612                .index
613                .eval_value(context)
614                .and_then(|v| context.get_variable_info(d.id)?.r#type.array.calc_index(&v));
615            (d.id, idx)
616        });
617        self.expr
618            .gather_ff(context, table, decl, assign_target, false);
619        for dst in &self.dst {
620            dst.gather_ff_comb_assign(context, table, decl);
621        }
622    }
623
624    pub fn set_index(&mut self, index: &VarIndex) {
625        for dst in &mut self.dst {
626            dst.set_index(index);
627        }
628        self.expr.set_index(index);
629    }
630}
631
632impl fmt::Display for AssignStatement {
633    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
634        let mut ret = String::new();
635
636        if self.dst.len() == 1 {
637            ret.push_str(&format!("{} = {};", self.dst[0], self.expr));
638        } else {
639            ret.push_str(&format!("{{{}", self.dst[0]));
640            for d in &self.dst[1..] {
641                ret.push_str(&format!(", {}", d));
642            }
643            ret.push_str(&format!("}} = {};", self.expr));
644        }
645        ret.fmt(f)
646    }
647}
648
649#[derive(Clone)]
650pub struct IfStatement {
651    pub cond: Expression,
652    pub true_side: Vec<Statement>,
653    pub false_side: Vec<Statement>,
654    pub token: TokenRange,
655}
656
657impl IfStatement {
658    pub fn eval_value(&self, context: &mut Context) -> ControlFlow {
659        if let Some(cond) = self.cond.eval_value(context) {
660            if cond.to_usize().unwrap_or(0) != 0 {
661                for stmt in &self.true_side {
662                    if stmt.eval_value(context) == ControlFlow::Break {
663                        return ControlFlow::Break;
664                    }
665                }
666            } else {
667                for stmt in &self.false_side {
668                    if stmt.eval_value(context) == ControlFlow::Break {
669                        return ControlFlow::Break;
670                    }
671                }
672            }
673        }
674        ControlFlow::Continue
675    }
676
677    pub fn insert_leaf_false(&mut self, false_side: Vec<Statement>) {
678        if self.false_side.is_empty() {
679            self.false_side = false_side;
680        } else if let Statement::If(x) = &mut self.false_side[0] {
681            x.insert_leaf_false(false_side);
682        }
683    }
684
685    pub fn eval_assign(
686        &self,
687        context: &mut Context,
688        assign_table: &mut AssignTable,
689        assign_context: AssignContext,
690        base_tables: &[&AssignTable],
691    ) {
692        let mut true_table = AssignTable::new(context);
693        let mut false_table = AssignTable::new(context);
694
695        std::mem::swap(&mut true_table.refernced, &mut assign_table.refernced);
696
697        let base_tables = if assign_table.table.is_empty() {
698            Cow::Borrowed(base_tables)
699        } else {
700            let mut base_tables = base_tables.to_vec();
701            base_tables.push(assign_table);
702            Cow::Owned(base_tables)
703        };
704
705        for x in &self.true_side {
706            x.eval_assign(context, &mut true_table, assign_context, &base_tables);
707        }
708
709        std::mem::swap(&mut false_table.refernced, &mut true_table.refernced);
710        for x in &self.false_side {
711            x.eval_assign(context, &mut false_table, assign_context, &base_tables);
712        }
713
714        if assign_context.is_comb() && !has_cond_type(&self.token) {
715            true_table.check_uncoverd(context, &false_table, &base_tables);
716        }
717
718        true_table.merge_by_or(context, &mut false_table, false);
719        assign_table.merge_by_or(context, &mut true_table, false);
720        std::mem::swap(&mut assign_table.refernced, &mut false_table.refernced);
721    }
722
723    pub fn gather_ff(&self, context: &mut Context, table: &mut FfTable, decl: usize) {
724        self.cond.gather_ff(context, table, decl, None, true);
725        for x in &self.true_side {
726            x.gather_ff(context, table, decl);
727        }
728        for x in &self.false_side {
729            x.gather_ff(context, table, decl);
730        }
731    }
732
733    pub fn set_index(&mut self, index: &VarIndex) {
734        self.cond.set_index(index);
735        for x in &mut self.true_side {
736            x.set_index(index);
737        }
738        for x in &mut self.false_side {
739            x.set_index(index);
740        }
741    }
742}
743
744impl fmt::Display for IfStatement {
745    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
746        let mut ret = format!("if {} {{\n", self.cond);
747        for x in &self.true_side {
748            let text = format!("{}\n", x);
749            ret.push_str(&indent_all_by(2, text));
750        }
751        ret.push('}');
752        if !self.false_side.is_empty() {
753            ret.push_str(" else {\n");
754            for x in &self.false_side {
755                let text = format!("{}\n", x);
756                ret.push_str(&indent_all_by(2, text));
757            }
758            ret.push('}');
759        }
760        ret.fmt(f)
761    }
762}
763
764#[derive(Clone)]
765pub struct IfResetStatement {
766    pub true_side: Vec<Statement>,
767    pub false_side: Vec<Statement>,
768    pub token: TokenRange,
769}
770
771impl IfResetStatement {
772    pub fn eval_assign(
773        &self,
774        context: &mut Context,
775        assign_table: &mut AssignTable,
776        base_tables: &[&AssignTable],
777    ) {
778        let mut true_table = AssignTable::new(context);
779        let mut false_table = AssignTable::new(context);
780
781        std::mem::swap(&mut true_table.refernced, &mut assign_table.refernced);
782
783        let mut base_tables = base_tables.to_vec();
784        base_tables.push(assign_table);
785
786        for x in &self.true_side {
787            x.eval_assign(context, &mut true_table, AssignContext::Ff, &base_tables);
788        }
789
790        std::mem::swap(&mut false_table.refernced, &mut true_table.refernced);
791        for x in &self.false_side {
792            x.eval_assign(context, &mut false_table, AssignContext::Ff, &base_tables);
793        }
794
795        if !allow_missing_reset_statement(&self.token) {
796            true_table.check_missing_reset(context, &false_table);
797        }
798
799        true_table.merge_by_or(context, &mut false_table, false);
800        assign_table.merge_by_or(context, &mut true_table, false);
801        std::mem::swap(&mut assign_table.refernced, &mut false_table.refernced);
802    }
803
804    pub fn gather_ff(&self, context: &mut Context, table: &mut FfTable, decl: usize) {
805        for x in &self.true_side {
806            x.gather_ff(context, table, decl);
807        }
808        for x in &self.false_side {
809            x.gather_ff(context, table, decl);
810        }
811    }
812
813    pub fn set_index(&mut self, index: &VarIndex) {
814        for x in &mut self.true_side {
815            x.set_index(index);
816        }
817        for x in &mut self.false_side {
818            x.set_index(index);
819        }
820    }
821}
822
823impl fmt::Display for IfResetStatement {
824    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
825        let mut ret = "if_reset {\n".to_string();
826        for x in &self.true_side {
827            let text = format!("{}\n", x);
828            ret.push_str(&indent_all_by(2, text));
829        }
830        ret.push('}');
831        if !self.false_side.is_empty() {
832            ret.push_str(" else {\n");
833            for x in &self.false_side {
834                let text = format!("{}\n", x);
835                ret.push_str(&indent_all_by(2, text));
836            }
837            ret.push('}');
838        }
839        ret.fmt(f)
840    }
841}