1use super::dist::{DefaultWeight, Dist, Item, SELECTOR_BASE, Span};
39use super::func::{self, Func, FuncError};
40use super::guard::{self, GuardValue};
41use super::ir::{
42 ArrayId, ArrayNode, Assignment, BinOp, ConstraintEvalError, Expr, LoopId, ReduceOp, Ty, UnOp,
43 VarId, eval, mask,
44};
45use super::solve::{SolveError, Solver};
46use num_bigint::BigUint;
47use num_traits::{One, Zero};
48use std::collections::HashMap;
49
50pub const SIZE_TIERS: [u64; 2] = [1024, 65536];
52
53pub const MAX_SIZE_RETRIES: usize = 256;
56
57#[derive(Clone, Debug)]
59pub struct ArrayState {
60 pub elem_ty: Ty,
62 pub active: bool,
65 pub cur: Vec<Option<BigUint>>,
67 pub domain: Option<Vec<BigUint>>,
69 pub rows: Option<RowsState>,
73 pub template_of: Option<ArrayId>,
76}
77
78#[derive(Clone, Debug)]
81pub struct RowsState {
82 pub levels: u32,
86 pub templates: Vec<ArrayId>,
90 pub cur_rows: Vec<Vec<Option<BigUint>>>,
93 pub deeper: Vec<ArrayState>,
95 pub row_base: ArrayId,
98 pub lowered: bool,
100}
101
102impl RowsState {
103 pub fn new(
105 levels: u32,
106 cur_rows: Vec<Vec<Option<BigUint>>>,
107 deeper: Vec<ArrayState>,
108 ) -> RowsState {
109 RowsState {
110 levels,
111 templates: vec![u32::MAX; levels as usize],
112 cur_rows,
113 deeper,
114 row_base: 0,
115 lowered: false,
116 }
117 }
118
119 pub fn set_template(&mut self, level: usize, t: ArrayId) {
122 if let Some(slot) = self.templates.get_mut(level) {
123 *slot = t;
124 }
125 if level > 0 {
126 for d in &mut self.deeper {
127 if let Some(r) = d.rows.as_mut() {
128 r.set_template(level - 1, t);
129 }
130 }
131 }
132 }
133}
134
135impl ArrayState {
136 pub fn flat(elem_ty: Ty, active: bool, cur: Vec<Option<BigUint>>) -> ArrayState {
138 ArrayState {
139 elem_ty,
140 active,
141 cur,
142 domain: None,
143 rows: None,
144 template_of: None,
145 }
146 }
147
148 pub fn with_domain(mut self, domain: Option<Vec<BigUint>>) -> ArrayState {
150 self.domain = domain;
151 self
152 }
153}
154
155#[derive(Clone, Debug, PartialEq, Eq)]
157pub enum ExpandError {
158 IndexOutOfRange {
160 array: ArrayId,
162 index: i64,
164 size: usize,
166 },
167 LeafIndexOutOfRange {
170 index: i64,
172 size: usize,
174 },
175 NonConstantIndex,
178 GuardError,
180 StateUnknown(u32),
182 Func(FuncError),
184 DistRandomBound,
188 Malformed(&'static str),
190}
191
192#[derive(Clone, Debug, Default)]
194pub struct Expanded {
195 pub conjuncts: Vec<Expr>,
198 pub elem_vars: Vec<(ArrayId, usize)>,
200 pub elem_tys: Vec<Ty>,
202 pub extra_states: Vec<Option<BigUint>>,
205 pub extra_dists: Vec<Dist>,
210}
211
212struct Expander<'a> {
213 arrays: &'a [ArrayState],
214 sizes: &'a [usize],
215 elem_base: Vec<u32>,
217 asg: Assignment,
218 synth: HashMap<(ArrayId, usize), u32>,
219 env: Vec<(LoopId, Expr)>,
220 funcs: &'a [Func],
221 dists: &'a [Dist],
223 extra_dists: Vec<Dist>,
224 instanced: Vec<InstancedDist>,
229 rebind: Vec<(ArrayId, ArrayId)>,
232}
233
234type InstancedDist = (usize, Vec<(LoopId, Expr)>, usize);
236
237fn konst(t: Ty, v: u64) -> Expr {
238 Expr::constant(t, v)
239}
240
241fn has_vars(e: &Expr) -> bool {
242 let mut any = false;
243 e.for_each_var(&mut |_, _| any = true);
244 any
245}
246
247impl Expander<'_> {
248 fn array(&self, a: ArrayId) -> Result<&ArrayState, ExpandError> {
249 self.arrays.get(a as usize).ok_or(ExpandError::Malformed(
250 "an array id outside the program's table",
251 ))
252 }
253
254 fn res(&self, a: ArrayId) -> ArrayId {
256 self.rebind
257 .iter()
258 .rev()
259 .find(|(t, _)| *t == a)
260 .map_or(a, |(_, r)| *r)
261 }
262
263 fn size_of(&self, a: ArrayId) -> Result<usize, ExpandError> {
264 let a = self.res(a);
265 self.sizes
266 .get(a as usize)
267 .copied()
268 .ok_or(ExpandError::Malformed("no size for an array"))
269 }
270
271 fn const_value(&self, e: &Expr) -> Result<BigUint, ExpandError> {
273 if has_vars(e) {
274 return Err(ExpandError::NonConstantIndex);
275 }
276 eval(e, &self.asg).map_err(|err| match err {
277 ConstraintEvalError::StateUnknown(s) => ExpandError::StateUnknown(s),
278 _ => ExpandError::NonConstantIndex,
279 })
280 }
281
282 fn index_value(&mut self, index: &Expr) -> Result<i64, ExpandError> {
285 let idx = self.expand(index)?;
286 let v = self.const_value(&idx)?;
287 let t = idx.ty();
288 Ok(if t.signed && v.bit(u64::from(t.width) - 1) {
289 -i64::try_from((BigUint::one() << t.width) - &v).unwrap_or(i64::MAX)
290 } else {
291 i64::try_from(&v).unwrap_or(i64::MAX)
292 })
293 }
294
295 fn element(&mut self, a: ArrayId, idx: usize, ty: Ty) -> Result<Expr, ExpandError> {
298 let a = self.res(a);
299 let arr = self.array(a)?;
300 if arr.elem_ty != ty {
301 return Err(ExpandError::Malformed("an element read at the wrong type"));
302 }
303 let (active, cur) = (arr.active, arr.cur.get(idx).cloned());
304 if active {
305 let id = self.elem_base[a as usize] + u32::try_from(idx).expect("an element index");
306 return Ok(Expr::Var(id, ty));
307 }
308 let Some(cur) = cur else {
309 return Err(ExpandError::Malformed("an element of a stale array view"));
310 };
311 if let Some(&id) = self.synth.get(&(a, idx)) {
312 return Ok(Expr::State(id, ty));
313 }
314 let id = u32::try_from(self.asg.states.len()).expect("a state id");
315 self.asg.states.push(cur);
316 self.synth.insert((a, idx), id);
317 Ok(Expr::State(id, ty))
318 }
319
320 fn lookup(&self, id: LoopId) -> Result<Expr, ExpandError> {
321 self.env
322 .iter()
323 .rev()
324 .find(|(l, _)| *l == id)
325 .map(|(_, e)| e.clone())
326 .ok_or(ExpandError::Malformed("a loop variable outside its loop"))
327 }
328
329 fn expand(&mut self, e: &Expr) -> Result<Expr, ExpandError> {
330 match e {
331 Expr::Guard(p, then, otherwise, t) => {
332 let p = self.expand(p)?;
333 match guard::value(&p, &self.asg) {
334 GuardValue::True => self.expand(then),
335 GuardValue::False => self.expand(otherwise),
336 GuardValue::Error => Err(ExpandError::GuardError),
337 GuardValue::Random => Ok(Expr::Guard(
338 Box::new(p),
339 Box::new(self.expand(then)?),
340 Box::new(self.expand(otherwise)?),
341 *t,
342 )),
343 }
344 }
345 Expr::Ite(c, x, y, t) => {
346 let c = self.expand(c)?;
347 if !has_vars(&c)
348 && let Ok(v) = eval(&c, &self.asg)
349 {
350 return if v.is_zero() {
351 self.expand(y)
352 } else {
353 self.expand(x)
354 };
355 }
356 Ok(Expr::Ite(
357 Box::new(c),
358 Box::new(self.expand(x)?),
359 Box::new(self.expand(y)?),
360 *t,
361 ))
362 }
363 Expr::Binary(op @ (BinOp::LogAnd | BinOp::LogOr | BinOp::LogImpl), l, r, t) => {
364 let l = self.expand(l)?;
365 if !has_vars(&l)
366 && let Ok(v) = eval(&l, &self.asg)
367 {
368 let l_true = !v.is_zero();
369 let decided = match op {
370 BinOp::LogAnd | BinOp::LogImpl => !l_true,
371 _ => l_true,
372 };
373 if decided {
374 return Ok(konst(*t, u64::from(*op != BinOp::LogAnd)));
375 }
376 }
377 Ok(Expr::Binary(
378 *op,
379 Box::new(l),
380 Box::new(self.expand(r)?),
381 *t,
382 ))
383 }
384 Expr::Array(n) => self.array_node(n),
385 Expr::Var(v, t) if *v >= SELECTOR_BASE => {
386 let j = (*v - SELECTOR_BASE) as usize;
387 match self.dists.get(j) {
388 Some(d) if d.subject.is_some() => {
389 let k = match self
390 .instanced
391 .iter()
392 .find(|(tj, env, _)| *tj == j && *env == self.env)
393 {
394 Some(&(_, _, k)) => k,
395 None => {
396 let inst = self.instantiate(d)?;
397 self.extra_dists.push(inst);
398 let k = self.extra_dists.len() - 1;
399 self.instanced.push((j, self.env.clone(), k));
400 k
401 }
402 };
403 Ok(Expr::Var(SELECTOR_BASE + (self.dists.len() + k) as u32, *t))
404 }
405 _ => Ok(e.clone()),
406 }
407 }
408 other => other.try_map_children(&mut |c| self.expand(c)),
409 }
410 }
411
412 fn instantiate(&mut self, d: &Dist) -> Result<Dist, ExpandError> {
417 let Some(subject) = &d.subject else {
418 return Err(ExpandError::Malformed("an instance of a plain dist"));
419 };
420 let subject = self.expand(subject)?;
421 let mut vars: Vec<VarId> = Vec::new();
422 subject.for_each_var(&mut |v, _| vars.push(v));
423 vars.sort_unstable();
424 vars.dedup();
425 let plain_var = match &subject {
426 Expr::Var(v, _) => Some(*v),
427 _ => None,
428 };
429 let fixed = |this: &mut Self, e: &Expr| -> Result<Expr, ExpandError> {
430 let x = this.expand(e)?;
431 if has_vars(&x) {
432 return Err(ExpandError::DistRandomBound);
433 }
434 Ok(x)
435 };
436 let mut items = Vec::with_capacity(d.items.len());
437 for it in &d.items {
438 let span = match &it.span {
439 Span::Point(v) => Span::Point(fixed(self, v)?),
440 Span::Range { lo, hi } => Span::Range {
441 lo: lo.as_ref().map(|b| fixed(self, b)).transpose()?,
442 hi: hi.as_ref().map(|b| fixed(self, b)).transpose()?,
443 },
444 };
445 items.push(Item {
446 member: konst(Ty::BIT, 1),
447 ty: it.ty,
448 span,
449 weight: fixed(self, &it.weight)?,
450 per_range: it.per_range,
451 });
452 }
453 let default = match &d.default {
454 Some(w) => Some(DefaultWeight {
455 weight: fixed(self, &w.weight)?,
456 per_range: w.per_range,
457 }),
458 None => None,
459 };
460 Ok(Dist {
461 subject_ty: d.subject_ty,
462 plain_var,
463 vars,
464 items,
465 default,
466 subject: None,
467 })
468 }
469
470 fn row_array(&mut self, template: ArrayId, row: &Expr) -> Result<ArrayId, ExpandError> {
473 let outer = self
474 .array(template)?
475 .template_of
476 .ok_or(ExpandError::Malformed("a row scope over a non-template"))?;
477 let outer = self.res(outer);
480 let k = self.index_value(row)?;
481 let size = self.size_of(outer)?;
482 if k < 0 || k as usize >= size {
483 return Err(ExpandError::IndexOutOfRange {
484 array: outer,
485 index: k,
486 size,
487 });
488 }
489 let base = self
490 .array(outer)?
491 .rows
492 .as_ref()
493 .ok_or(ExpandError::Malformed("rows of an array without rows"))?
494 .row_base;
495 Ok(base + k as ArrayId)
496 }
497
498 fn array_node(&mut self, n: &ArrayNode) -> Result<Expr, ExpandError> {
499 match n {
500 ArrayNode::Call { func, args, ty } => {
501 let args = args
502 .iter()
503 .map(|a| self.expand(a))
504 .collect::<Result<Vec<_>, _>>()?;
505 let call = Expr::Array(ArrayNode::Call {
506 func: *func,
507 args,
508 ty: *ty,
509 });
510 func::fold_ground_calls(&call, self.funcs, &self.asg.states).map_err(|e| match e {
513 FuncError::Eval(ConstraintEvalError::StateUnknown(s)) => {
514 ExpandError::StateUnknown(s)
515 }
516 other => ExpandError::Func(other),
517 })
518 }
519 ArrayNode::Size(a) => Ok(konst(Ty::signed(32), self.size_of(*a)? as u64)),
520 ArrayNode::Loop(id, t) => {
521 let v = self.lookup(*id)?;
522 if v.ty() != *t {
523 return Err(ExpandError::Malformed(
524 "a loop variable bound at another type",
525 ));
526 }
527 Ok(v)
528 }
529 ArrayNode::Pick {
530 index,
531 low,
532 elems,
533 ty,
534 } => {
535 let i = self.index_value(index)?;
536 let pos = i
537 .checked_sub(*low)
538 .and_then(|p| usize::try_from(p).ok())
539 .filter(|&p| p < elems.len());
540 let Some(pos) = pos else {
541 return Err(ExpandError::LeafIndexOutOfRange {
542 index: i,
543 size: elems.len(),
544 });
545 };
546 let x = self.expand(&elems[pos])?;
547 if x.ty() != *ty {
548 return Err(ExpandError::Malformed("a pick element of another type"));
549 }
550 Ok(x)
551 }
552 ArrayNode::Elem { array, index, ty } => {
553 let i = self.index_value(index)?;
554 let size = self.size_of(*array)?;
555 if i < 0 || i as usize >= size {
556 return Err(ExpandError::IndexOutOfRange {
557 array: self.res(*array),
558 index: i,
559 size,
560 });
561 }
562 self.element(*array, i as usize, *ty)
563 }
564 ArrayNode::RowScope {
565 template,
566 row,
567 body,
568 } => {
569 let target = self.row_array(*template, row)?;
570 self.rebind.push((*template, target));
571 let r = self.expand(body);
572 self.rebind.pop();
573 r
574 }
575 ArrayNode::Distinct { scalars, arrays } => {
576 let mut members = Vec::new();
577 for s in scalars {
578 members.push(self.expand(s)?);
579 }
580 for a in arrays {
581 let size = self.size_of(*a)?;
582 let elem_ty = self.array(self.res(*a))?.elem_ty;
583 for i in 0..size {
584 members.push(self.element(*a, i, elem_ty)?);
585 }
586 }
587 if members.iter().any(|m| m.ty() != members[0].ty()) {
588 return Err(ExpandError::Malformed(
589 "the members of a unique group are of different types",
590 ));
591 }
592 Ok(if members.len() < 2 {
593 konst(Ty::BIT, 1)
594 } else {
595 Expr::AllDistinct(members)
596 })
597 }
598 ArrayNode::Foreach { array, var, body } => {
599 let size = self.size_of(*array)?;
600 let mut acc: Option<Expr> = None;
601 for i in 0..size {
602 self.env.push((*var, konst(Ty::signed(32), i as u64)));
603 let b = self.expand(body);
604 self.env.pop();
605 let b = b?;
606 acc = Some(match acc {
607 None => b,
608 Some(a) => Expr::Binary(BinOp::LogAnd, Box::new(a), Box::new(b), Ty::BIT),
609 });
610 }
611 Ok(acc.unwrap_or_else(|| konst(Ty::BIT, 1)))
612 }
613 ArrayNode::Reduce {
614 op,
615 array,
616 var,
617 index_var,
618 body,
619 } => {
620 let size = self.size_of(*array)?;
621 let elem_ty = self.array(self.res(*array))?.elem_ty;
622 let ty = body.ty();
623 let mut acc: Option<Expr> = None;
624 for i in 0..size {
625 let item = self.element(*array, i, elem_ty)?;
626 self.env.push((*var, item));
627 self.env.push((*index_var, konst(Ty::signed(32), i as u64)));
628 let b = self.expand(body);
629 self.env.pop();
630 self.env.pop();
631 let b = b?;
632 acc = Some(match acc {
633 None => b,
634 Some(a) => {
635 let bop = match op {
636 ReduceOp::Sum => BinOp::Add,
637 ReduceOp::Product => BinOp::Mul,
638 ReduceOp::And => BinOp::And,
639 ReduceOp::Or => BinOp::Or,
640 ReduceOp::Xor => BinOp::Xor,
641 };
642 Expr::Binary(bop, Box::new(a), Box::new(b), ty)
643 }
644 });
645 }
646 Ok(acc.unwrap_or_else(|| {
647 Expr::Const(
648 ty,
649 match op {
650 ReduceOp::Product => BigUint::one(),
651 ReduceOp::And => mask(ty.width),
652 ReduceOp::Sum | ReduceOp::Or | ReduceOp::Xor => BigUint::zero(),
653 },
654 )
655 }))
656 }
657 }
658 }
659}
660
661fn domain_constraint(v: Expr, domain: &[BigUint]) -> Expr {
663 let t = v.ty();
664 let mut acc: Option<Expr> = None;
665 for d in domain {
666 let eq = Expr::Binary(
667 BinOp::Eq,
668 Box::new(v.clone()),
669 Box::new(Expr::Const(t, d.clone() & mask(t.width))),
670 Ty::BIT,
671 );
672 acc = Some(match acc {
673 None => eq,
674 Some(a) => Expr::Binary(BinOp::LogOr, Box::new(a), Box::new(eq), Ty::BIT),
675 });
676 }
677 acc.unwrap_or_else(|| konst(Ty::BIT, 0))
678}
679
680pub fn expand(
686 conjuncts: &[Expr],
687 arrays: &[ArrayState],
688 sizes: &[usize],
689 n_vars0: u32,
690 states: &[Option<BigUint>],
691) -> Result<Expanded, ExpandError> {
692 expand_with(conjuncts, arrays, sizes, n_vars0, states, &[], &[])
693}
694
695pub fn expand_with(
700 conjuncts: &[Expr],
701 arrays: &[ArrayState],
702 sizes: &[usize],
703 n_vars0: u32,
704 states: &[Option<BigUint>],
705 funcs: &[Func],
706 dists: &[Dist],
707) -> Result<Expanded, ExpandError> {
708 if arrays.len() != sizes.len() {
709 return Err(ExpandError::Malformed("one size per array"));
710 }
711 let mut out = Expanded::default();
712 let mut elem_base = vec![0u32; arrays.len()];
713 let mut next = n_vars0;
714 for (a, arr) in arrays.iter().enumerate() {
715 if !arr.active {
716 continue;
717 }
718 elem_base[a] = next;
719 for i in 0..sizes[a] {
720 out.elem_vars.push((a as ArrayId, i));
721 out.elem_tys.push(arr.elem_ty);
722 next += 1;
723 if let Some(dom) = &arr.domain {
724 let id = elem_base[a] + i as u32;
725 out.conjuncts
726 .push(domain_constraint(Expr::Var(id, arr.elem_ty), dom));
727 }
728 }
729 }
730 let mut ex = Expander {
731 arrays,
732 sizes,
733 elem_base,
734 asg: Assignment {
735 vars: Vec::new(),
736 states: states.to_vec(),
737 },
738 synth: HashMap::new(),
739 env: Vec::new(),
740 funcs,
741 dists,
742 extra_dists: Vec::new(),
743 instanced: Vec::new(),
744 rebind: Vec::new(),
745 };
746 for c in conjuncts {
747 let e = ex.expand(c)?;
748 out.conjuncts.push(e);
749 }
750 out.extra_dists = std::mem::take(&mut ex.extra_dists);
751 out.extra_states = ex.asg.states.split_off(states.len());
752 Ok(out)
753}
754
755#[derive(Clone, Debug)]
757pub struct Lowered {
758 pub conjuncts: Vec<Expr>,
761 pub arrays: Vec<ArrayState>,
764 pub bases: Vec<(ArrayId, ArrayId)>,
766}
767
768struct Lowerer<'a> {
769 arrays: &'a [ArrayState],
771 n_in: usize,
774 sizes: &'a [usize],
775 states: &'a [Option<BigUint>],
776 bases: &'a [(ArrayId, ArrayId)],
777 below: &'a [(ArrayId, ArrayId, ArrayId)],
780 env: Vec<(LoopId, i64)>,
781 rebind: Vec<(ArrayId, ArrayId)>,
782}
783
784impl Lowerer<'_> {
785 fn res(&self, a: ArrayId) -> ArrayId {
786 self.rebind
787 .iter()
788 .rev()
789 .find(|(t, _)| *t == a)
790 .map_or(a, |(_, r)| *r)
791 }
792
793 fn base_of(&self, outer: ArrayId) -> Option<ArrayId> {
794 self.bases
795 .iter()
796 .find(|(o, _)| *o == outer)
797 .map(|(_, b)| *b)
798 }
799
800 fn fixed(&self, row: &Expr) -> Option<i64> {
804 let mut stale = false;
805 let row = replace_sizes(row, &mut |a| {
806 let a = self.res(a);
807 stale |= a as usize >= self.n_in;
808 konst(
809 Ty::signed(32),
810 self.sizes.get(a as usize).copied().unwrap_or(0) as u64,
811 )
812 });
813 if stale {
814 return None;
815 }
816 let asg = Assignment {
817 vars: Vec::new(),
818 states: self.states.to_vec(),
819 };
820 let v = eval(&row, &asg).ok()?;
821 let t = row.ty();
822 Some(if t.signed && v.bit(u64::from(t.width) - 1) {
823 -i64::try_from((BigUint::one() << t.width) - &v).ok()?
824 } else {
825 i64::try_from(&v).ok()?
826 })
827 }
828
829 fn low(&mut self, e: &Expr) -> Result<Expr, ExpandError> {
830 let Expr::Array(n) = e else {
831 return e.try_map_children(&mut |c| self.low(c));
832 };
833 match n {
834 ArrayNode::Loop(id, t) => Ok(match self.env.iter().rev().find(|(l, _)| l == id) {
835 Some(&(_, i)) => Expr::constant(*t, i as u64),
836 None => e.clone(),
837 }),
838 ArrayNode::Size(a) => Ok(Expr::Array(ArrayNode::Size(self.res(*a)))),
839 ArrayNode::Elem { array, index, ty } => Ok(Expr::Array(ArrayNode::Elem {
840 array: self.res(*array),
841 index: Box::new(self.low(index)?),
842 ty: *ty,
843 })),
844 ArrayNode::Distinct { scalars, arrays } => Ok(Expr::Array(ArrayNode::Distinct {
845 scalars: scalars
846 .iter()
847 .map(|x| self.low(x))
848 .collect::<Result<_, _>>()?,
849 arrays: arrays.iter().map(|a| self.res(*a)).collect(),
850 })),
851 ArrayNode::Reduce {
852 op,
853 array,
854 var,
855 index_var,
856 body,
857 } => Ok(Expr::Array(ArrayNode::Reduce {
858 op: *op,
859 array: self.res(*array),
860 var: *var,
861 index_var: *index_var,
862 body: Box::new(self.low(body)?),
863 })),
864 ArrayNode::Foreach { array, var, body } => {
865 let a = self.res(*array);
866 if self.base_of(a).is_none() {
867 return Ok(Expr::Array(ArrayNode::Foreach {
868 array: a,
869 var: *var,
870 body: Box::new(self.low(body)?),
871 }));
872 }
873 let mut acc: Option<Expr> = None;
874 for i in 0..self.sizes[a as usize] {
875 self.env.push((*var, i as i64));
876 let b = self.low(body);
877 self.env.pop();
878 let b = b?;
879 acc = Some(match acc {
880 None => b,
881 Some(x) => Expr::Binary(BinOp::LogAnd, Box::new(x), Box::new(b), Ty::BIT),
882 });
883 }
884 Ok(acc.unwrap_or_else(|| konst(Ty::BIT, 1)))
885 }
886 ArrayNode::RowScope {
887 template,
888 row,
889 body,
890 } => {
891 let row = self.low(row)?;
892 let raw = *template;
898 let bound = self.res(raw);
899 let tpl = if self
900 .arrays
901 .get(bound as usize)
902 .is_some_and(|t| t.template_of.is_some())
903 {
904 bound
905 } else {
906 raw
907 };
908 let outer = self
909 .arrays
910 .get(tpl as usize)
911 .and_then(|t| t.template_of)
912 .map(|o| self.res(o))
913 .ok_or(ExpandError::Malformed("a row scope over a non-template"))?;
914 let depth = self.rebind.len();
915 let (Some(k), Some(base)) = (self.fixed(&row), self.base_of(outer)) else {
916 self.rebind.push((raw, tpl));
920 self.rebind.push((tpl, tpl));
921 let body = self.low(body);
922 self.rebind.truncate(depth);
923 return Ok(Expr::Array(ArrayNode::RowScope {
924 template: tpl,
925 row: Box::new(row),
926 body: Box::new(body?),
927 }));
928 };
929 let size = self.sizes[outer as usize];
930 if k < 0 || k as usize >= size {
931 return Err(ExpandError::IndexOutOfRange {
932 array: outer,
933 index: k,
934 size,
935 });
936 }
937 let picked = base + k as ArrayId;
938 self.rebind.push((raw, picked));
939 self.rebind.push((tpl, picked));
940 for &(t, r, below) in self.below {
941 if r == picked {
942 self.rebind.push((t, below));
943 }
944 }
945 let r = self.low(body);
946 self.rebind.truncate(depth);
947 r
948 }
949 ArrayNode::Call { .. } | ArrayNode::Pick { .. } => {
950 e.try_map_children(&mut |c| self.low(c))
951 }
952 }
953 }
954}
955
956pub fn lower_rows(
964 conjuncts: &[Expr],
965 arrays: &[ArrayState],
966 sizes: &[usize],
967 states: &[Option<BigUint>],
968) -> Result<Lowered, ExpandError> {
969 let mut out: Vec<ArrayState> = arrays.to_vec();
970 let mut bases = Vec::new();
971 let mut below = Vec::new();
972 let mut sizes2: Vec<usize> = sizes.to_vec();
973 for (a, arr) in arrays.iter().enumerate() {
974 let Some(rows) = arr.rows.as_ref().filter(|r| !r.lowered) else {
975 continue;
976 };
977 let n = sizes[a];
978 let base = out.len() as ArrayId;
979 bases.push((a as ArrayId, base));
980 for k in 0..n {
981 let row = if rows.levels <= 1 {
982 ArrayState {
983 elem_ty: arr.elem_ty,
984 active: arr.active,
985 cur: rows.cur_rows.get(k).cloned().unwrap_or_default(),
986 domain: arr.domain.clone(),
987 rows: None,
988 template_of: None,
989 }
990 } else {
991 rows.deeper.get(k).cloned().unwrap_or_else(|| ArrayState {
992 elem_ty: arr.elem_ty,
993 active: arr.active,
994 cur: Vec::new(),
995 domain: arr.domain.clone(),
996 rows: Some(RowsState::new(rows.levels - 1, Vec::new(), Vec::new())),
997 template_of: None,
998 })
999 };
1000 sizes2.push(row.cur.len());
1001 out.push(row);
1002 }
1003 if rows.levels > 1 {
1007 for k in 0..n {
1008 let row = base + k as ArrayId;
1009 let shared = rows.templates.get(1).copied().unwrap_or(u32::MAX);
1010 let r = out[row as usize].rows.as_mut().expect("a row with rows");
1011 r.templates = rows.templates[1..].to_vec();
1012 if shared == u32::MAX {
1013 continue;
1014 }
1015 let tid = out.len() as ArrayId;
1016 out[row as usize].rows.as_mut().expect("rows").templates[0] = tid;
1017 below.push((shared, row, tid));
1018 out.push(ArrayState {
1019 elem_ty: arr.elem_ty,
1020 active: false,
1021 cur: Vec::new(),
1022 domain: None,
1023 rows: None,
1024 template_of: Some(row),
1025 });
1026 sizes2.push(0);
1027 }
1028 }
1029 out[a].active = false;
1030 out[a].cur = vec![Some(BigUint::zero()); n];
1031 if let Some(r) = out[a].rows.as_mut() {
1032 r.row_base = base;
1033 r.lowered = true;
1034 }
1035 }
1036 let mut lw = Lowerer {
1037 arrays: &out,
1038 n_in: arrays.len(),
1039 sizes: &sizes2,
1040 states,
1041 bases: &bases,
1042 below: &below,
1043 env: Vec::new(),
1044 rebind: Vec::new(),
1045 };
1046 let lowered = conjuncts
1047 .iter()
1048 .map(|c| lw.low(c))
1049 .collect::<Result<Vec<_>, _>>()?;
1050 Ok(Lowered {
1051 conjuncts: lowered,
1052 arrays: out,
1053 bases,
1054 })
1055}
1056
1057#[derive(Clone, Debug, PartialEq, Eq)]
1059pub enum SizeError {
1060 Unsatisfiable,
1062 TooLarge,
1064 GuardError,
1066 Solve(SolveError),
1068}
1069
1070#[derive(Clone, Debug, PartialEq, Eq)]
1072pub struct SizeSolution {
1073 pub sizes: Vec<usize>,
1075 pub sized: Vec<ArrayId>,
1077 pub tuple: Vec<usize>,
1079}
1080
1081fn size_eligible(c: &Expr) -> bool {
1084 !c.any_node(&mut |n| match n {
1085 Expr::Array(
1086 ArrayNode::Elem { .. }
1087 | ArrayNode::Loop(..)
1088 | ArrayNode::Foreach { .. }
1089 | ArrayNode::Reduce { .. }
1090 | ArrayNode::Distinct { .. }
1091 | ArrayNode::Pick { .. }
1093 | ArrayNode::RowScope { .. }
1095 | ArrayNode::Call { .. },
1098 ) => true,
1099 Expr::Var(v, _) => *v >= super::dist::SELECTOR_BASE,
1100 _ => false,
1101 })
1102}
1103
1104fn replace_sizes(e: &Expr, f: &mut impl FnMut(ArrayId) -> Expr) -> Expr {
1105 match e {
1106 Expr::Array(ArrayNode::Size(a)) => f(*a),
1107 other => other
1108 .try_map_children::<()>(&mut |c| Ok(replace_sizes(c, f)))
1109 .expect("infallible"),
1110 }
1111}
1112
1113fn find(parent: &mut [usize], x: usize) -> usize {
1114 let mut r = x;
1115 while parent[r] != r {
1116 r = parent[r];
1117 }
1118 let mut c = x;
1119 while parent[c] != r {
1120 let n = parent[c];
1121 parent[c] = r;
1122 c = n;
1123 }
1124 r
1125}
1126
1127pub fn solve_sizes(
1133 conjuncts: &[Expr],
1134 arrays: &[ArrayState],
1135 var_tys: &[Ty],
1136 states: &[Option<BigUint>],
1137 blocked: &[Vec<usize>],
1138 seed: &mut u64,
1139) -> Result<SizeSolution, SizeError> {
1140 let current: Vec<usize> = arrays.iter().map(|a| a.cur.len()).collect();
1141 let n_fields = var_tys.len() as u32;
1142 let mut sized: Vec<ArrayId> = Vec::new();
1144 let mut eligible: Vec<&Expr> = Vec::new();
1145 for c in conjuncts {
1146 if !size_eligible(c) {
1147 continue;
1148 }
1149 c.any_node(&mut |n| {
1150 if let Expr::Array(ArrayNode::Size(a)) = n
1151 && arrays.get(*a as usize).is_some_and(|x| x.active)
1152 && !sized.contains(a)
1153 {
1154 sized.push(*a);
1155 }
1156 false
1157 });
1158 eligible.push(c);
1159 }
1160 if sized.is_empty() {
1161 return Ok(SizeSolution {
1162 sizes: current,
1163 sized,
1164 tuple: Vec::new(),
1165 });
1166 }
1167 sized.sort_unstable();
1168 let size_var = |a: ArrayId| -> u32 {
1169 n_fields + u32::try_from(sized.iter().position(|x| *x == a).expect("sized")).unwrap()
1170 };
1171 let total = n_fields as usize + sized.len();
1173 let subbed: Vec<Expr> = eligible
1174 .iter()
1175 .map(|c| {
1176 replace_sizes(c, &mut |a| {
1177 if arrays[a as usize].active {
1178 Expr::Var(size_var(a), Ty::signed(32))
1179 } else {
1180 konst(Ty::signed(32), current[a as usize] as u64)
1181 }
1182 })
1183 })
1184 .collect();
1185 let mut parent: Vec<usize> = (0..total).collect();
1186 let vars_of: Vec<Vec<VarId>> = subbed
1187 .iter()
1188 .map(|c| {
1189 let mut v = Vec::new();
1190 c.for_each_var(&mut |x, _| v.push(x));
1191 v
1192 })
1193 .collect();
1194 for vs in &vars_of {
1195 for w in vs.windows(2) {
1196 let (a, b) = (
1197 find(&mut parent, w[0] as usize),
1198 find(&mut parent, w[1] as usize),
1199 );
1200 parent[a] = b;
1201 }
1202 }
1203 let size_roots: Vec<usize> = (0..sized.len())
1204 .map(|k| find(&mut parent, n_fields as usize + k))
1205 .collect();
1206 let mut keep: Vec<Expr> = Vec::new();
1207 let mut used = vec![false; total];
1208 for k in 0..sized.len() {
1209 used[n_fields as usize + k] = true;
1210 }
1211 for (c, vs) in subbed.iter().zip(&vars_of) {
1212 let Some(&v0) = vs.first() else { continue };
1213 let root = find(&mut parent, v0 as usize);
1214 if size_roots.contains(&root) {
1215 for &v in vs {
1216 used[v as usize] = true;
1217 }
1218 keep.push(c.clone());
1219 }
1220 }
1221 let mut new_id = vec![0u32; total];
1223 let mut tys: Vec<Ty> = Vec::new();
1224 for (v, u) in used.iter().enumerate() {
1225 if *u {
1226 new_id[v] = tys.len() as u32;
1227 tys.push(if v < n_fields as usize {
1228 var_tys[v]
1229 } else {
1230 Ty::signed(32)
1231 });
1232 }
1233 }
1234 let mut keep: Vec<Expr> = keep
1235 .iter()
1236 .map(|c| c.substitute_vars(&mut |v, t| Expr::Var(new_id[v as usize], t)))
1237 .collect();
1238 let svar = |k: usize| Expr::Var(new_id[n_fields as usize + k], Ty::signed(32));
1239 for tup in blocked {
1241 let mut same: Option<Expr> = None;
1242 for (k, &sz) in tup.iter().enumerate() {
1243 let eq = Expr::Binary(
1244 BinOp::Eq,
1245 Box::new(svar(k)),
1246 Box::new(konst(Ty::signed(32), sz as u64)),
1247 Ty::BIT,
1248 );
1249 same = Some(match same {
1250 None => eq,
1251 Some(a) => Expr::Binary(BinOp::LogAnd, Box::new(a), Box::new(eq), Ty::BIT),
1252 });
1253 }
1254 if let Some(s) = same {
1255 keep.push(Expr::Unary(UnOp::LogNot, Box::new(s), Ty::BIT));
1256 }
1257 }
1258 let assignment = Assignment {
1259 vars: Vec::new(),
1260 states: states.to_vec(),
1261 };
1262 let order: Vec<VarId> = (0..tys.len() as u32).collect();
1263 let tiers: [u64; 3] = [SIZE_TIERS[0], SIZE_TIERS[1], i32::MAX as u64];
1264 for (ti, bound) in tiers.iter().enumerate() {
1265 let mut cs = keep.clone();
1266 for k in 0..sized.len() {
1267 cs.push(Expr::Binary(
1268 BinOp::Ge,
1269 Box::new(svar(k)),
1270 Box::new(konst(Ty::signed(32), 0)),
1271 Ty::BIT,
1272 ));
1273 cs.push(Expr::Binary(
1274 BinOp::Le,
1275 Box::new(svar(k)),
1276 Box::new(konst(Ty::signed(32), *bound)),
1277 Ty::BIT,
1278 ));
1279 }
1280 let mut resolved = Vec::with_capacity(cs.len());
1281 for c in &cs {
1282 resolved.push(guard::resolve(c, &assignment).map_err(|_| SizeError::GuardError)?);
1283 }
1284 let solver = match Solver::new(&tys, states, &resolved) {
1285 Ok(s) => s,
1286 Err(SolveError::Unsatisfiable) => continue,
1287 Err(e) => return Err(SizeError::Solve(e)),
1288 };
1289 let values = match solver.sample(&order, seed) {
1290 Ok(v) => v,
1291 Err(SolveError::Unsatisfiable) => continue,
1292 Err(e) => return Err(SizeError::Solve(e)),
1293 };
1294 if ti == 2 {
1295 return Err(SizeError::TooLarge);
1296 }
1297 let mut sizes = current;
1298 let mut tuple = Vec::with_capacity(sized.len());
1299 for (k, &a) in sized.iter().enumerate() {
1300 let v = &values[new_id[n_fields as usize + k] as usize];
1301 let n = usize::try_from(v).expect("a bounded size");
1302 sizes[a as usize] = n;
1303 tuple.push(n);
1304 }
1305 return Ok(SizeSolution {
1306 sizes,
1307 sized,
1308 tuple,
1309 });
1310 }
1311 Err(SizeError::Unsatisfiable)
1312}
1313
1314#[cfg(test)]
1315mod tests {
1316 use super::*;
1317
1318 fn s32() -> Ty {
1319 Ty::signed(32)
1320 }
1321 fn u(w: u32) -> Ty {
1322 Ty::unsigned(w)
1323 }
1324 fn size(a: ArrayId) -> Expr {
1325 Expr::Array(ArrayNode::Size(a))
1326 }
1327 fn elem(a: ArrayId, i: Expr, t: Ty) -> Expr {
1328 Expr::Array(ArrayNode::Elem {
1329 array: a,
1330 index: Box::new(i),
1331 ty: t,
1332 })
1333 }
1334 fn lp(id: LoopId) -> Expr {
1335 Expr::Array(ArrayNode::Loop(id, s32()))
1336 }
1337 fn bin(op: BinOp, a: Expr, b: Expr, t: Ty) -> Expr {
1338 Expr::Binary(op, Box::new(a), Box::new(b), t)
1339 }
1340 fn arr(active: bool, cur: &[u64]) -> ArrayState {
1341 ArrayState {
1342 elem_ty: s32(),
1343 active,
1344 cur: cur.iter().map(|v| Some(BigUint::from(*v))).collect(),
1345 domain: None,
1346 rows: None,
1347 template_of: None,
1348 }
1349 }
1350 fn ev(e: &Expr, vars: &[u64], states: &[Option<BigUint>]) -> bool {
1351 let a = Assignment {
1352 vars: vars.iter().map(|v| BigUint::from(*v)).collect(),
1353 states: states.to_vec(),
1354 };
1355 !eval(e, &a).expect("evaluates").is_zero()
1356 }
1357
1358 #[test]
1361 fn foreach_unrolls_over_the_size() {
1362 let body = bin(
1363 BinOp::Gt,
1364 elem(0, lp(0), s32()),
1365 bin(BinOp::Mul, konst(s32(), 2), lp(0), s32()),
1366 Ty::BIT,
1367 );
1368 let c = Expr::Array(ArrayNode::Foreach {
1369 array: 0,
1370 var: 0,
1371 body: Box::new(body),
1372 });
1373 let x = expand(&[c], &[arr(true, &[])], &[3], 0, &[]).expect("expands");
1374 assert_eq!(x.elem_vars, vec![(0, 0), (0, 1), (0, 2)]);
1375 assert_eq!(x.conjuncts.len(), 1);
1376 assert!(ev(&x.conjuncts[0], &[1, 3, 5], &[]));
1377 assert!(!ev(&x.conjuncts[0], &[1, 2, 5], &[]));
1378 assert!(!ev(&x.conjuncts[0], &[0, 3, 5], &[]));
1379 }
1380
1381 #[test]
1384 fn a_constant_guard_keeps_the_last_neighbour_unread() {
1385 let pred = bin(
1386 BinOp::Lt,
1387 lp(0),
1388 bin(BinOp::Sub, size(0), konst(s32(), 1), s32()),
1389 Ty::BIT,
1390 );
1391 let then = bin(
1392 BinOp::Lt,
1393 elem(0, lp(0), s32()),
1394 elem(0, bin(BinOp::Add, lp(0), konst(s32(), 1), s32()), s32()),
1395 Ty::BIT,
1396 );
1397 let body = Expr::Guard(
1398 Box::new(pred),
1399 Box::new(then),
1400 Box::new(konst(Ty::BIT, 1)),
1401 Ty::BIT,
1402 );
1403 let c = Expr::Array(ArrayNode::Foreach {
1404 array: 0,
1405 var: 0,
1406 body: Box::new(body),
1407 });
1408 let x = expand(&[c], &[arr(true, &[])], &[4], 0, &[]).expect("expands");
1409 assert_eq!(x.elem_vars.len(), 4);
1410 assert!(ev(&x.conjuncts[0], &[1, 2, 3, 4], &[]));
1411 assert!(!ev(&x.conjuncts[0], &[1, 3, 2, 4], &[]));
1412 }
1413
1414 #[test]
1415 fn an_unguarded_out_of_range_index_is_a_loud_error() {
1416 let c = bin(
1417 BinOp::Eq,
1418 elem(0, konst(s32(), 5), s32()),
1419 konst(s32(), 0),
1420 Ty::BIT,
1421 );
1422 assert_eq!(
1423 expand(&[c], &[arr(true, &[])], &[2], 0, &[]).unwrap_err(),
1424 ExpandError::IndexOutOfRange {
1425 array: 0,
1426 index: 5,
1427 size: 2
1428 }
1429 );
1430 }
1431
1432 #[test]
1435 fn a_pick_selects_the_element_its_index_names() {
1436 let pick = |idx: Expr| {
1437 Expr::Array(ArrayNode::Pick {
1438 index: Box::new(idx),
1439 low: 2,
1440 elems: vec![Expr::Var(0, u(8)), Expr::Var(1, u(8)), Expr::Var(2, u(8))],
1441 ty: u(8),
1442 })
1443 };
1444 let states = [Some(BigUint::from(3u32))];
1445 let c = bin(
1446 BinOp::Eq,
1447 pick(Expr::State(0, s32())),
1448 konst(u(8), 9),
1449 Ty::BIT,
1450 );
1451 let x = expand(&[c], &[], &[], 3, &states).expect("expands");
1452 assert_eq!(x.conjuncts.len(), 1);
1453 assert!(ev(&x.conjuncts[0], &[0, 9, 0], &states));
1454 assert!(!ev(&x.conjuncts[0], &[9, 0, 9], &states));
1455 }
1456
1457 #[test]
1461 fn an_out_of_range_pick_is_an_error_only_when_live() {
1462 let pick = Expr::Array(ArrayNode::Pick {
1463 index: Box::new(Expr::State(0, s32())),
1464 low: 0,
1465 elems: vec![Expr::Var(0, u(8)), Expr::Var(1, u(8))],
1466 ty: u(8),
1467 });
1468 let body = bin(BinOp::Eq, pick, konst(u(8), 1), Ty::BIT);
1469 let guarded = Expr::Guard(
1470 Box::new(bin(
1471 BinOp::Lt,
1472 Expr::State(0, s32()),
1473 konst(s32(), 2),
1474 Ty::BIT,
1475 )),
1476 Box::new(body.clone()),
1477 Box::new(konst(Ty::BIT, 1)),
1478 Ty::BIT,
1479 );
1480 let out = [Some(BigUint::from(7u32))];
1481 let x = expand(&[guarded], &[], &[], 2, &out).expect("the guard drops the pick");
1482 assert!(ev(&x.conjuncts[0], &[], &out));
1483 assert_eq!(
1484 expand(std::slice::from_ref(&body), &[], &[], 2, &out).unwrap_err(),
1485 ExpandError::LeafIndexOutOfRange { index: 7, size: 2 }
1486 );
1487 let neg = [Some((BigUint::from(1u32) << 32) - 1u32)];
1489 assert_eq!(
1490 expand(&[body], &[], &[], 2, &neg).unwrap_err(),
1491 ExpandError::LeafIndexOutOfRange { index: -1, size: 2 }
1492 );
1493 }
1494
1495 #[test]
1498 fn an_inactive_array_is_state() {
1499 let c = bin(
1500 BinOp::Eq,
1501 elem(0, konst(s32(), 1), s32()),
1502 konst(s32(), 7),
1503 Ty::BIT,
1504 );
1505 let x = expand(&[c], &[arr(false, &[3, 7])], &[2], 0, &[]).expect("expands");
1506 assert!(x.elem_vars.is_empty());
1507 assert_eq!(x.extra_states, vec![Some(BigUint::from(7u32))]);
1508 assert!(ev(&x.conjuncts[0], &[], &x.extra_states));
1509 }
1510
1511 #[test]
1512 fn reductions_fold_and_empty_ones_are_the_identity() {
1513 let sum = |op| {
1514 Expr::Array(ArrayNode::Reduce {
1515 op,
1516 array: 0,
1517 var: 0,
1518 index_var: 1,
1519 body: Box::new(Expr::Array(ArrayNode::Loop(0, s32()))),
1520 })
1521 };
1522 let c = bin(BinOp::Eq, sum(ReduceOp::Sum), konst(s32(), 6), Ty::BIT);
1523 let x = expand(&[c], &[arr(true, &[])], &[3], 0, &[]).expect("expands");
1524 assert!(ev(&x.conjuncts[0], &[1, 2, 3], &[]));
1525 assert!(!ev(&x.conjuncts[0], &[1, 2, 4], &[]));
1526 for (op, want) in [
1527 (ReduceOp::Sum, 0u64),
1528 (ReduceOp::Product, 1),
1529 (ReduceOp::Or, 0),
1530 (ReduceOp::Xor, 0),
1531 (ReduceOp::And, 0xFFFF_FFFF),
1532 ] {
1533 let c = bin(BinOp::Eq, sum(op), konst(s32(), want), Ty::BIT);
1534 let x = expand(&[c], &[arr(true, &[])], &[0], 0, &[]).expect("expands");
1535 assert!(ev(&x.conjuncts[0], &[], &[]), "{op:?}");
1536 }
1537 }
1538
1539 #[test]
1541 fn a_size_constraint_resizes_the_array() {
1542 let c = bin(
1543 BinOp::Eq,
1544 size(0),
1545 Expr::Resize(Box::new(Expr::Var(0, u(8))), s32()),
1546 Ty::BIT,
1547 );
1548 let bound = bin(BinOp::Lt, Expr::Var(0, u(8)), konst(u(8), 5), Ty::BIT);
1549 let mut seen = std::collections::BTreeSet::new();
1550 let mut seed = 1u64;
1551 for _ in 0..200 {
1552 let s = solve_sizes(
1553 &[c.clone(), bound.clone()],
1554 &[arr(true, &[9, 9])],
1555 &[u(8)],
1556 &[],
1557 &[],
1558 &mut seed,
1559 )
1560 .expect("solves");
1561 assert_eq!(s.sized, vec![0]);
1562 assert!(s.sizes[0] < 5);
1563 seen.insert(s.sizes[0]);
1564 }
1565 assert_eq!(seen.len(), 5, "every size 0..5 is reachable: {seen:?}");
1566 }
1567
1568 #[test]
1569 fn an_unconstrained_size_keeps_the_array() {
1570 let mut seed = 1u64;
1571 let s = solve_sizes(&[], &[arr(true, &[1, 2, 3])], &[], &[], &[], &mut seed).unwrap();
1572 assert_eq!(s.sizes, vec![3]);
1573 assert!(s.sized.is_empty());
1574 }
1575
1576 #[test]
1577 fn an_impossible_size_is_unsatisfiable_and_a_huge_one_is_too_large() {
1578 let mut seed = 1u64;
1579 let lt = |n: u64| bin(BinOp::Lt, size(0), konst(s32(), n), Ty::BIT);
1580 let gt = |n: u64| bin(BinOp::Gt, size(0), konst(s32(), n), Ty::BIT);
1581 let a = [arr(true, &[])];
1582 assert_eq!(
1583 solve_sizes(&[lt(3), gt(5)], &a, &[], &[], &[], &mut seed).unwrap_err(),
1584 SizeError::Unsatisfiable
1585 );
1586 assert_eq!(
1587 solve_sizes(&[gt(10_000_000)], &a, &[], &[], &[], &mut seed).unwrap_err(),
1588 SizeError::TooLarge
1589 );
1590 let s = solve_sizes(&[gt(2000)], &a, &[], &[], &[], &mut seed).unwrap();
1592 assert!(s.sizes[0] > 2000);
1593 }
1594
1595 #[test]
1596 fn blocked_tuples_are_excluded() {
1597 let c = bin(BinOp::Le, size(0), konst(s32(), 1), Ty::BIT);
1598 let a = [arr(true, &[])];
1599 let mut seed = 3u64;
1600 let s = solve_sizes(
1601 std::slice::from_ref(&c),
1602 &a,
1603 &[],
1604 &[],
1605 &[vec![0]],
1606 &mut seed,
1607 )
1608 .unwrap();
1609 assert_eq!(s.sizes, vec![1]);
1610 assert_eq!(
1611 solve_sizes(&[c], &a, &[], &[], &[vec![0], vec![1]], &mut seed).unwrap_err(),
1612 SizeError::Unsatisfiable
1613 );
1614 }
1615
1616 #[test]
1619 fn the_size_problem_holds_only_connected_size_constraints() {
1620 let elem_c = bin(
1621 BinOp::Eq,
1622 elem(0, konst(s32(), 0), s32()),
1623 konst(s32(), 1),
1624 Ty::BIT,
1625 );
1626 let unrelated = bin(BinOp::Eq, Expr::Var(0, u(8)), konst(u(8), 200), Ty::BIT);
1627 let c = bin(BinOp::Eq, size(0), konst(s32(), 2), Ty::BIT);
1628 let mut seed = 1u64;
1629 let s = solve_sizes(
1630 &[elem_c, unrelated, c],
1631 &[arr(true, &[])],
1632 &[u(8)],
1633 &[],
1634 &[],
1635 &mut seed,
1636 )
1637 .unwrap();
1638 assert_eq!(s.sizes, vec![2]);
1639 }
1640}