1use super::real::strip_conversions;
43use super::*;
44use crate::constraints::ir::{ArrayId, ArrayNode, LoopId, ReduceOp};
45use sv_lang::KnownSystemName;
46use sv_lang::kinds::SymbolKind;
47use sv_lang::nodes::LoopConstraintSyntax;
48
49#[derive(Clone, Debug, PartialEq, Eq)]
51pub struct ArrayInfo {
52 pub symbol: SymbolId,
54 pub name: String,
56 pub elem_ty: Ty,
58 pub rand: bool,
62 pub elem_domain: Option<Vec<BigUint>>,
65 pub assoc: bool,
68}
69
70#[derive(Clone, Debug)]
72pub(super) enum LoopBind {
73 Runtime(LoopId, Ty),
75 Const(i64),
77 Elem(Expr),
80}
81
82#[derive(Clone, Debug)]
84pub(super) struct LoopScope {
85 name: String,
86 value: LoopBind,
89 index: Option<LoopBind>,
91}
92
93#[derive(Clone, Copy, Debug, PartialEq, Eq)]
95pub(super) enum ArrayShape {
96 Collection,
98 Assoc,
100 Fixed,
102}
103
104pub(super) fn array_shape(t: Type<'_>) -> Option<ArrayShape> {
105 let c = t.canonical();
106 if c.is_associative_array() {
107 Some(ArrayShape::Assoc)
108 } else if c.is_dynamically_sized_array() || c.is_queue() {
109 Some(ArrayShape::Collection)
110 } else if c.is_unpacked_array() {
111 Some(ArrayShape::Fixed)
112 } else {
113 None
114 }
115}
116
117fn bad_array(what: impl Into<String>) -> BuildError {
118 BuildError::BadArray { what: what.into() }
119}
120
121pub(super) fn const_i64(ty: Ty, v: &BigUint) -> Option<i64> {
123 let signed = BigInt::from(v.clone());
124 let signed = if ty.signed && v.bit(u64::from(ty.width) - 1) {
125 signed - (BigInt::one() << ty.width)
126 } else {
127 signed
128 };
129 i64::try_from(signed).ok()
130}
131
132impl Builder<'_> {
133 pub(super) fn arrays_allowed(&self, what: &str) -> Result<(), BuildError> {
134 match self.no_arrays {
135 None => Ok(()),
136 Some(inside) => Err(unsupported(
137 format!("{what} inside {inside}"),
138 "C14 (array-level values have no weight or bound at build time)",
139 )),
140 }
141 }
142
143 pub fn declare_rand_array(&mut self, sym: Symbol<'_>) -> Result<u32, BuildError> {
147 let uid = sym.uid();
148 let name = sym.name().to_string();
149 match sym.rand_mode() {
150 RandMode::None => return Err(BuildError::NotRand { name }),
151 RandMode::RandC => {
152 return Err(unsupported(
153 format!("the `randc` array `{name}`: per-element cycling"),
154 "C14",
155 ));
156 }
157 RandMode::Rand => {}
158 }
159 if let Some(&id) = self.array_ix.get(&uid) {
160 self.arrays[id as usize].rand = true;
161 return Ok(id);
162 }
163 let vt = sym.value_type().ok_or_else(|| BuildError::Missing {
164 what: format!("value type for `{name}`"),
165 })?;
166 let info = self.array_info(uid.clone(), name, vt, true)?;
167 let id = self.arrays.len() as u32;
168 self.arrays.push(info);
169 self.array_ix.insert(uid, id);
170 Ok(id)
171 }
172
173 pub fn declare_array_forced(&mut self, sym: Symbol<'_>) -> Result<u32, BuildError> {
178 let uid = sym.uid();
179 let name = sym.name().to_string();
180 if matches!(sym.rand_mode(), RandMode::RandC) {
181 return Err(unsupported(
182 format!("the `randc` array `{name}`: per-element cycling"),
183 "C14",
184 ));
185 }
186 if let Some(&id) = self.array_ix.get(&uid) {
187 self.arrays[id as usize].rand = true;
188 return Ok(id);
189 }
190 let vt = sym.value_type().ok_or_else(|| BuildError::Missing {
191 what: format!("value type for `{name}`"),
192 })?;
193 let info = self.array_info(uid.clone(), name, vt, true)?;
194 let id = self.arrays.len() as u32;
195 self.arrays.push(info);
196 self.array_ix.insert(uid, id);
197 Ok(id)
198 }
199
200 fn array_info(
201 &self,
202 symbol: SymbolId,
203 name: String,
204 vt: Type<'_>,
205 rand: bool,
206 ) -> Result<ArrayInfo, BuildError> {
207 match array_shape(vt) {
208 Some(ArrayShape::Collection) => {}
209 Some(ArrayShape::Assoc) if rand => {}
213 Some(ArrayShape::Assoc) => return Err(Self::assoc_operand(&name)),
214 Some(ArrayShape::Fixed) => {
218 return Err(unsupported(
219 format!(
220 "the fixed-size unpacked array `{name}` as a whole-array constraint \
221 operand (name its elements, or use a reduction method or `foreach`)"
222 ),
223 "C14",
224 ));
225 }
226 None => {
227 return Err(BuildError::NonIntegral {
228 what: format!(
229 "`{name}` of type `{}`, which is not an array",
230 vt.to_sv_string()
231 ),
232 });
233 }
234 }
235 let et = vt
236 .canonical()
237 .element_type()
238 .ok_or_else(|| BuildError::Missing {
239 what: format!("element type of the array `{name}`"),
240 })?;
241 let elem_ty = ty_of(Some(et), &format!("an element of the array `{name}`"))?;
242 let elem_domain = Self::enum_values(et, elem_ty)?;
243 Ok(ArrayInfo {
244 symbol,
245 name,
246 elem_ty,
247 rand,
248 elem_domain,
249 assoc: array_shape(vt) == Some(ArrayShape::Assoc),
250 })
251 }
252
253 fn assoc_operand(name: &str) -> BuildError {
254 bad_array(format!(
255 "`{name}` is an associative array: its size and indices are not randomizable and \
256 its elements are not constraint operands (IEEE 1800-2023 18.4)"
257 ))
258 }
259
260 pub(super) fn array_of(&mut self, e: Expression<'_>) -> Result<ArrayId, BuildError> {
264 let e = strip_conversions(e);
265 let sym = match e.kind() {
266 ExpressionKind::NamedValue | ExpressionKind::HierarchicalValue => e.referenced_symbol(),
267 ExpressionKind::MemberAccess => {
268 let base = Self::operand(e, "a member access")?;
269 let is_this = base.kind() == ExpressionKind::NamedValue
270 && base.referenced_symbol().is_some_and(|s| s.name() == "this");
271 if !is_this {
272 return Err(BuildError::ForeignObjectAccess {
273 what: format!(
274 "the array `.{}` of another object",
275 e.member_symbol()
276 .map_or(String::new(), |m| m.name().to_string())
277 ),
278 });
279 }
280 e.member_symbol()
281 }
282 other => {
283 return Err(unsupported(
284 format!("a `{other:?}` expression as an array operand"),
285 "C14",
286 ));
287 }
288 };
289 let sym = sym.ok_or_else(|| BuildError::Missing {
290 what: "symbol of an array operand".into(),
291 })?;
292 let uid = sym.uid();
293 if let Some(&id) = self.array_ix.get(&uid) {
294 if self.arrays[id as usize].assoc {
295 return Err(Self::assoc_operand(&self.arrays[id as usize].name));
296 }
297 return Ok(id);
298 }
299 let vt = e.expr_type().ok_or_else(|| BuildError::Missing {
300 what: format!("type of the array `{}`", sym.name()),
301 })?;
302 let info = self.array_info(uid.clone(), sym.name().to_string(), vt, false)?;
303 let id = self.arrays.len() as u32;
304 self.arrays.push(info);
305 self.array_ix.insert(uid, id);
306 Ok(id)
307 }
308
309 pub(super) fn size_call_array<'d>(e: Expression<'d>) -> Option<Expression<'d>> {
312 let sub = e.system_subroutine()?;
313 if !matches!(
314 sub.known_name_id(),
315 KnownSystemName::Size | KnownSystemName::ArraySize | KnownSystemName::Num
316 ) {
317 return None;
318 }
319 let arr = *e.operands().first()?;
320 array_shape(arr.expr_type()?).map(|_| arr)
321 }
322
323 pub(super) fn array_size(&mut self, arr: Expression<'_>) -> Result<Expr, BuildError> {
325 self.arrays_allowed("an array size")?;
326 let id = self.array_of(arr)?;
327 Ok(Expr::Array(ArrayNode::Size(id)))
328 }
329
330 pub(super) fn array_element(
332 &mut self,
333 e: Expression<'_>,
334 base: Expression<'_>,
335 ty: Ty,
336 ) -> Result<Expr, BuildError> {
337 let shape = base.expr_type().and_then(array_shape);
338 let sel = e.selector().ok_or_else(|| BuildError::Missing {
339 what: "index of an element select".into(),
340 })?;
341 match shape {
342 Some(ArrayShape::Collection) => {
343 self.arrays_allowed("an array element")?;
344 let id = self.array_of(base)?;
345 let index = self.expr(sel)?;
346 let mut has_var = false;
347 index.for_each_var(&mut |_, _| has_var = true);
348 if has_var {
349 return Err(bad_array(format!(
350 "a random variable in the index of the array `{}`: an index is a \
351 constant, a state variable, a loop variable or the size (IEEE \
352 1800-2023 18.5.7.1)",
353 self.arrays[id as usize].name
354 )));
355 }
356 let elem_ty = self.arrays[id as usize].elem_ty;
357 let el = Expr::Array(ArrayNode::Elem {
358 array: id,
359 index: Box::new(index),
360 ty: elem_ty,
361 });
362 Ok(if elem_ty == ty {
363 el
364 } else {
365 Expr::Resize(Box::new(el), ty)
366 })
367 }
368 Some(ArrayShape::Assoc) => Err(bad_array(
369 "an element of an associative array: its indices are not randomizable and it \
370 has no constraint form (IEEE 1800-2023 18.4)",
371 )),
372 Some(ArrayShape::Fixed) => self.fixed_element(e),
373 None => Err(BuildError::Missing {
374 what: "array type of an element select base".into(),
375 }),
376 }
377 }
378
379 fn fixed_element(&mut self, e: Expression<'_>) -> Result<Expr, BuildError> {
384 let what = e
385 .select_value()
386 .map(strip_conversions)
387 .and_then(|b| b.referenced_symbol().or_else(|| b.member_symbol()))
388 .map_or_else(String::new, |s| s.name().to_string());
389 Err(unsupported(
390 format!(
391 "an element of the unpacked array `{what}`, which is not a property (or a \
392 member of a struct property) of this class"
393 ),
394 "C14",
395 ))
396 }
397
398 fn foreach_vars(&self, c: Constraint<'_>) -> Result<Vec<String>, BuildError> {
401 for tree in self.trees {
402 if let Some(node) = c.syntax(tree) {
403 let l = LoopConstraintSyntax::cast(node).ok_or_else(|| BuildError::Missing {
404 what: "`foreach` syntax for a Foreach constraint".into(),
405 })?;
406 return Ok(l
407 .loop_list()
408 .loop_variables()
409 .iter()
410 .map(|v| v.syntax().text().trim().to_string())
411 .collect());
412 }
413 }
414 Err(BuildError::Missing {
415 what: "syntax node for a constraint (is its syntax tree passed to the builder?)".into(),
416 })
417 }
418
419 fn fresh_loop(&mut self) -> LoopId {
420 let id = self.next_loop;
421 self.next_loop += 1;
422 id
423 }
424
425 fn in_scope(
427 &mut self,
428 scope: LoopScope,
429 body: Constraint<'_>,
430 ) -> Result<Vec<Expr>, BuildError> {
431 self.loops.push(scope);
432 let r = self.constraint(body);
433 self.loops.pop();
434 r
435 }
436
437 pub(super) fn foreach(&mut self, c: Constraint<'_>) -> Result<Vec<Expr>, BuildError> {
439 let names = self.foreach_vars(c)?;
440 let kids = c.children();
441 let arr =
442 kids.iter()
443 .find_map(|n| n.as_expression())
444 .ok_or_else(|| BuildError::Missing {
445 what: "array expression of a `foreach` constraint".into(),
446 })?;
447 let body =
448 kids.iter()
449 .find_map(|n| n.as_constraint())
450 .ok_or_else(|| BuildError::Missing {
451 what: "body of a `foreach` constraint".into(),
452 })?;
453 let at = arr.expr_type().ok_or_else(|| BuildError::Missing {
454 what: "type of the `foreach` array".into(),
455 })?;
456 let shape = array_shape(at);
457 let nested_fixed = shape == Some(ArrayShape::Fixed)
462 && at
463 .canonical()
464 .element_type()
465 .is_some_and(|elem| array_shape(elem) == Some(ArrayShape::Fixed));
466 if names.len() > 1 || nested_fixed {
467 let dims = self.static_dims(at, &names)?;
468 return self.unroll_nd(&dims, body);
469 }
470 let [name] = names.as_slice() else {
471 return Err(BuildError::Missing {
472 what: "a loop variable of a `foreach` constraint".into(),
473 });
474 };
475 if shape.is_some()
476 && at
477 .canonical()
478 .element_type()
479 .is_some_and(|elem| array_shape(elem).is_some())
480 {
481 return Err(unsupported("a `foreach` over an array of arrays", "C11"));
482 }
483 let int = Ty::signed(32);
484 match shape {
485 Some(ArrayShape::Collection) => {
486 self.arrays_allowed("a `foreach`")?;
487 let array = self.array_of(arr)?;
488 let var = self.fresh_loop();
489 let (r0, b0, rb0) = (
492 self.real_conds.len() + self.real_dists.len(),
493 self.before.len(),
494 self.real_before.len(),
495 );
496 let parts = self.in_scope(
497 LoopScope {
498 name: name.clone(),
499 value: LoopBind::Runtime(var, int),
500 index: None,
501 },
502 body,
503 )?;
504 if self.real_conds.len() + self.real_dists.len() != r0
505 || self.before.len() != b0
506 || self.real_before.len() != rb0
507 {
508 return Err(unsupported(
509 "a real `dist` or condition, or a `solve ... before` constraint, inside a \
510 `foreach` over a dynamic array or queue (it would have to hold once per \
511 element)",
512 "C11",
513 ));
514 }
515 Ok(vec![Expr::Array(ArrayNode::Foreach {
516 array,
517 var,
518 body: Box::new(and_all(parts)),
519 })])
520 }
521 Some(ArrayShape::Assoc) => Err(bad_array(
522 "a `foreach` over an associative array: its indices are not randomizable and \
523 it has no constraint form (IEEE 1800-2023 18.4)",
524 )),
525 Some(ArrayShape::Fixed) => {
526 let r = at.canonical().fixed_range();
527 self.unroll(name, i64::from(r.left), i64::from(r.right), body)
528 }
529 None => {
530 let width = u32::try_from(at.bit_width()).map_err(|_| BuildError::Missing {
532 what: "representable width for a `foreach` array".into(),
533 })?;
534 let g = Geom::of(at, width, "the `foreach` array")?;
535 self.unroll(name, g.left, g.right, body)
536 }
537 }
538 }
539
540 fn static_dims(
543 &self,
544 ty: sv_lang::Type<'_>,
545 names: &[String],
546 ) -> Result<Vec<(String, i64, i64)>, BuildError> {
547 let mut dims = Vec::new();
548 let mut cur = ty;
549 for (k, name) in names.iter().enumerate() {
550 let c = cur.canonical();
551 let (left, right) = match array_shape(cur) {
552 Some(ArrayShape::Fixed) => {
553 let r = c.fixed_range();
554 (i64::from(r.left), i64::from(r.right))
555 }
556 None if c.is_integral() => {
557 let width = u32::try_from(c.bit_width()).map_err(|_| BuildError::Missing {
558 what: "representable width for a `foreach` array".into(),
559 })?;
560 let g = Geom::of(cur, width, "the `foreach` array")?;
561 (g.left, g.right)
562 }
563 _ => {
564 return Err(unsupported(
565 format!(
566 "a multi-dimensional `foreach`: dimension {} is a dynamic array, queue or associative array (only fixed-size dimensions unroll)",
567 k + 1
568 ),
569 "C11",
570 ));
571 }
572 };
573 dims.push((name.clone(), left, right));
574 if k + 1 < names.len() {
575 cur = c.element_type().ok_or_else(|| {
576 unsupported(
577 format!(
578 "a `foreach` with {} loop variables over an array with fewer \
579 dimensions",
580 names.len()
581 ),
582 "C11",
583 )
584 })?;
585 }
586 }
587 Ok(dims)
588 }
589
590 fn unroll_nd(
593 &mut self,
594 dims: &[(String, i64, i64)],
595 body: Constraint<'_>,
596 ) -> Result<Vec<Expr>, BuildError> {
597 let Some((name, left, right)) = dims.first() else {
598 return self.constraint(body);
599 };
600 if name.is_empty() {
601 return self.unroll_nd(&dims[1..], body);
602 }
603 let idxs: Vec<i64> = if left >= right {
604 (*right..=*left).rev().collect()
605 } else {
606 (*left..=*right).collect()
607 };
608 let mut out = Vec::new();
609 for i in idxs {
610 self.loops.push(LoopScope {
611 name: name.clone(),
612 value: LoopBind::Const(i),
613 index: None,
614 });
615 let r = self.unroll_nd(&dims[1..], body);
616 self.loops.pop();
617 out.extend(r?);
618 }
619 Ok(out)
620 }
621
622 fn unroll(
625 &mut self,
626 name: &str,
627 left: i64,
628 right: i64,
629 body: Constraint<'_>,
630 ) -> Result<Vec<Expr>, BuildError> {
631 let idxs: Vec<i64> = if left >= right {
632 (right..=left).rev().collect()
633 } else {
634 (left..=right).collect()
635 };
636 let mut out = Vec::new();
637 for i in idxs {
638 out.extend(self.in_scope(
639 LoopScope {
640 name: name.to_string(),
641 value: LoopBind::Const(i),
642 index: None,
643 },
644 body,
645 )?);
646 }
647 Ok(out)
648 }
649
650 pub(super) fn in_runtime_loop(&self) -> bool {
654 self.loops
655 .iter()
656 .any(|s| matches!(s.value, LoopBind::Runtime(..)))
657 }
658
659 fn scope_of(&self, name: &str) -> Option<&LoopScope> {
660 self.loops.iter().rev().find(|s| s.name == name)
661 }
662
663 fn bind_expr(&self, b: LoopBind, ty: Ty) -> Result<Expr, BuildError> {
664 match b {
665 LoopBind::Runtime(id, lt) => {
666 let l = Expr::Array(ArrayNode::Loop(id, lt));
667 Ok(if lt == ty {
668 l
669 } else {
670 Expr::Resize(Box::new(l), ty)
671 })
672 }
673 LoopBind::Const(i) => Ok(iconst(ty, i)),
674 LoopBind::Elem(x) => Ok(if x.ty() == ty {
675 x
676 } else {
677 Expr::Resize(Box::new(x), ty)
678 }),
679 }
680 }
681
682 pub(super) fn loop_ref(&mut self, sym: Symbol<'_>, ty: Ty) -> Result<Expr, BuildError> {
684 debug_assert_eq!(sym.kind(), SymbolKind::Iterator);
685 let name = sym.name();
686 let b = self
687 .scope_of(name)
688 .ok_or_else(|| BuildError::Missing {
689 what: format!("an enclosing `foreach` or array method binding `{name}`"),
690 })?
691 .value
692 .clone();
693 if matches!(b, LoopBind::Runtime(..)) {
694 self.arrays_allowed("a loop variable")?;
695 }
696 self.bind_expr(b, ty)
697 }
698
699 pub(super) fn iterator_index(&mut self, e: Expression<'_>, ty: Ty) -> Result<Expr, BuildError> {
701 let this = *e.operands().first().ok_or_else(|| BuildError::Missing {
702 what: "iterator of `index`".into(),
703 })?;
704 let name = this
705 .referenced_symbol()
706 .map(|s| s.name().to_string())
707 .ok_or_else(|| BuildError::Missing {
708 what: "symbol of the iterator of `index`".into(),
709 })?;
710 let b = self
711 .scope_of(&name)
712 .and_then(|s| s.index.clone())
713 .ok_or_else(|| BuildError::Missing {
714 what: format!("an array method iterator `{name}` for `.index`"),
715 })?;
716 self.arrays_allowed("an iterator index")?;
717 self.bind_expr(b, ty)
718 }
719
720 pub(super) fn array_reduction(
723 &mut self,
724 e: Expression<'_>,
725 op: ReduceOp,
726 ty: Ty,
727 ) -> Result<Expr, BuildError> {
728 let arr = e
729 .operands()
730 .into_iter()
731 .find(|o| o.expr_type().is_some_and(|t| array_shape(t).is_some()))
732 .ok_or_else(|| BuildError::Missing {
733 what: "array operand of a reduction method".into(),
734 })?;
735 if arr.expr_type().and_then(array_shape) == Some(ArrayShape::Fixed) {
736 return self.fixed_reduction(e, arr, op, ty);
737 }
738 self.arrays_allowed("an array reduction")?;
739 let array = self.array_of(arr)?;
740 let elem_ty = self.arrays[array as usize].elem_ty;
741 let var = self.fresh_loop();
742 let index_var = self.fresh_loop();
743 let body = match e.iterator_expr() {
744 Some(w) => {
745 let iter = e.iterator_var().ok_or_else(|| BuildError::Missing {
746 what: "iterator variable of a reduction's `with` clause".into(),
747 })?;
748 self.loops.push(LoopScope {
749 name: iter.name().to_string(),
750 value: LoopBind::Runtime(var, elem_ty),
751 index: Some(LoopBind::Runtime(index_var, Ty::signed(32))),
752 });
753 let b = self.expr(w);
754 self.loops.pop();
755 b?
756 }
757 None => Expr::Array(ArrayNode::Loop(var, elem_ty)),
758 };
759 let body = if body.ty() == ty {
760 body
761 } else {
762 coerce(body, ty)
763 };
764 Ok(Expr::Array(ArrayNode::Reduce {
765 op,
766 array,
767 var,
768 index_var,
769 body: Box::new(body),
770 }))
771 }
772
773 fn fixed_reduction(
778 &mut self,
779 e: Expression<'_>,
780 arr: Expression<'_>,
781 op: ReduceOp,
782 ty: Ty,
783 ) -> Result<Expr, BuildError> {
784 let at = arr
785 .expr_type()
786 .ok_or_else(|| BuildError::Missing {
787 what: "type of the reduced array".into(),
788 })?
789 .canonical();
790 let r = at.fixed_unpacked_array_range();
791 let (lo, hi) = (
792 i64::from(r.left).min(i64::from(r.right)),
793 i64::from(r.left).max(i64::from(r.right)),
794 );
795 let et = at.element_type().ok_or_else(|| BuildError::Missing {
796 what: "element type of the reduced array".into(),
797 })?;
798 let elem_ty = ty_of(Some(et), "an element of the reduced array")?;
799 let mut acc: Option<Expr> = None;
800 for i in lo..=hi {
801 let item = self.aggregate_element(arr, i, elem_ty)?;
802 let body = match e.iterator_expr() {
803 Some(w) => {
804 let iter = e.iterator_var().ok_or_else(|| BuildError::Missing {
805 what: "iterator variable of a reduction's `with` clause".into(),
806 })?;
807 self.loops.push(LoopScope {
808 name: iter.name().to_string(),
809 value: LoopBind::Elem(item),
810 index: Some(LoopBind::Const(i)),
811 });
812 let b = self.expr(w);
813 self.loops.pop();
814 b?
815 }
816 None => item,
817 };
818 let body = if body.ty() == ty {
819 body
820 } else {
821 coerce(body, ty)
822 };
823 acc = Some(match acc {
824 None => body,
825 Some(a) => {
826 let bop = match op {
827 ReduceOp::Sum => BinOp::Add,
828 ReduceOp::Product => BinOp::Mul,
829 ReduceOp::And => BinOp::And,
830 ReduceOp::Or => BinOp::Or,
831 ReduceOp::Xor => BinOp::Xor,
832 };
833 bin(bop, a, body, ty)
834 }
835 });
836 }
837 acc.ok_or_else(|| BuildError::Missing {
838 what: "elements of the reduced array".into(),
839 })
840 }
841
842 pub(super) fn inside_array(
849 &mut self,
850 left: &Expr,
851 arr: Expression<'_>,
852 ) -> Result<Expr, BuildError> {
853 let eq = |left: &Expr, x: Expr| {
854 let t = common_ty(left.ty(), x.ty());
855 bin(BinOp::Eq, coerce(left.clone(), t), coerce(x, t), Ty::BIT)
856 };
857 let at = arr.expr_type().ok_or_else(|| BuildError::Missing {
858 what: "type of an `inside` array".into(),
859 })?;
860 match array_shape(at) {
861 Some(ArrayShape::Fixed) => {
862 let c = at.canonical();
863 let r = c.fixed_unpacked_array_range();
864 let (lo, hi) = (
865 i64::from(r.left).min(i64::from(r.right)),
866 i64::from(r.left).max(i64::from(r.right)),
867 );
868 let et = c.element_type().ok_or_else(|| BuildError::Missing {
869 what: "element type of an `inside` array".into(),
870 })?;
871 let elem_ty = ty_of(Some(et), "an element of an `inside` array")?;
872 let mut acc: Option<Expr> = None;
873 for i in lo..=hi {
874 let item = self.aggregate_element(arr, i, elem_ty)?;
875 let term = eq(left, item);
876 acc = Some(match acc {
877 None => term,
878 Some(prev) => bin(BinOp::LogOr, prev, term, Ty::BIT),
879 });
880 }
881 acc.ok_or_else(|| BuildError::Missing {
882 what: "elements of an `inside` array".into(),
883 })
884 }
885 Some(ArrayShape::Collection) => {
886 self.arrays_allowed("an `inside` over an array")?;
887 let array = self.array_of(arr)?;
888 let elem_ty = self.arrays[array as usize].elem_ty;
889 let var = self.fresh_loop();
890 let index_var = self.fresh_loop();
891 let body = eq(left, Expr::Array(ArrayNode::Loop(var, elem_ty)));
892 Ok(Expr::Array(ArrayNode::Reduce {
893 op: ReduceOp::Or,
894 array,
895 var,
896 index_var,
897 body: Box::new(body),
898 }))
899 }
900 Some(ArrayShape::Assoc) | None => Err(bad_array(
901 "an associative array in an `inside` set: its elements are not constraint \
902 operands (IEEE 1800-2023 18.4)",
903 )),
904 }
905 }
906
907 pub(super) fn array_call(
910 &mut self,
911 e: Expression<'_>,
912 ty: Ty,
913 ) -> Option<Result<Expr, BuildError>> {
914 let sub = e.system_subroutine()?;
915 let op = match sub.known_name_id() {
916 KnownSystemName::Size | KnownSystemName::ArraySize | KnownSystemName::Num => {
917 let arr = Self::size_call_array(e)?;
918 return Some(self.array_size(arr));
919 }
920 KnownSystemName::Index => return Some(self.iterator_index(e, ty)),
921 KnownSystemName::Sum => ReduceOp::Sum,
922 KnownSystemName::Product => ReduceOp::Product,
923 KnownSystemName::And => ReduceOp::And,
924 KnownSystemName::Or => ReduceOp::Or,
925 KnownSystemName::XOr => ReduceOp::Xor,
926 _ => return None,
927 };
928 Some(self.array_reduction(e, op, ty))
929 }
930}