1use super::dist::{DefaultWeight, Dist, Item, SELECTOR_BASE, Span};
100use super::func::Func;
101use super::ir::{BinOp, Expr, IrError, Ty, UnOp, VarId, mask};
102use super::order::{self, OrderError};
103use super::priority::{self, Disable, Priority, Soft, SoftKey};
104use super::randc::MAX_RANDC_WIDTH;
105use super::real_domain::{RealCond, RealDist, RealProblem};
106use super::reduce::BlockItems;
107use crate::objects::{FieldSlot, Window};
108use crate::symid::{SymbolId, SymbolUid};
109use core::fmt;
110use num_bigint::{BigInt, BigUint};
111use num_traits::{One, Zero};
112use std::collections::{HashMap, HashSet};
113use sv_lang::kinds::{ConstraintKind, ExpressionKind, PatternKind, SymbolKind};
114use sv_lang::nodes::{
115 ExpressionConstraintSyntax, SolveBeforeConstraintSyntax, ValueRangeExpressionSyntax,
116};
117use sv_lang::{
118 AstNode, BinaryOp, Bit, Constraint, ConstraintBlockFlags, DistWeightKind, Expression, RandMode,
119 SVInt, Symbol, SyntaxTree, Type, UnaryOp,
120};
121
122mod aggregate;
123mod arrays;
124mod funcs;
125mod real;
126mod unique;
127pub use aggregate::{Declared, Step, is_aggregate_type, property_is_random};
128pub use arrays::ArrayInfo;
129use arrays::LoopScope;
130pub use real::RealVarInfo;
131
132#[derive(Clone, Debug, PartialEq, Eq)]
134pub enum BuildError {
135 FourStateOperator {
137 op: &'static str,
139 },
140 FourStateLiteral {
142 text: String,
144 },
145 VirtualInterfaceRand {
147 name: String,
149 },
150 NonIntegral {
153 what: String,
155 },
156 ForeignObjectAccess {
159 what: String,
161 },
162 NotRand {
164 name: String,
166 },
167 Unsupported {
169 construct: String,
171 task: &'static str,
173 },
174 OutOfRangeSelect {
176 what: String,
178 },
179 Missing {
181 what: String,
183 },
184 RandcTooWide {
187 name: String,
189 width: u32,
191 max: u32,
193 },
194 BadSolveOperand {
197 what: String,
199 },
200 BadDist {
203 what: String,
205 },
206 BadUnique {
209 what: String,
211 },
212 RealConstraint {
216 what: String,
218 },
219 BadArray {
223 what: String,
225 },
226 BadFunction {
230 function: String,
232 what: String,
234 },
235 BadSoft {
238 what: String,
240 },
241 MissingExternBody {
244 name: String,
246 },
247 Order(OrderError),
249 Ir(IrError),
251}
252
253impl fmt::Display for BuildError {
254 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
255 match self {
256 BuildError::FourStateOperator { op } => write!(
257 f,
258 "the 4-state operator `{op}` is illegal in a constraint (IEEE 1800-2023 18.3)"
259 ),
260 BuildError::FourStateLiteral { text } => write!(
261 f,
262 "the 4-state value `{text}` is illegal in a constraint (IEEE 1800-2023 18.3)"
263 ),
264 BuildError::VirtualInterfaceRand { name } => write!(
265 f,
266 "`rand` variable `{name}` has a virtual interface type, which cannot be randomized"
267 ),
268 BuildError::NonIntegral { what } => {
269 write!(
270 f,
271 "{what} is not an integral value; constraints solve integral variables only"
272 )
273 }
274 BuildError::ForeignObjectAccess { what } => write!(
275 f,
276 "{what}: this reference to another object's members is not covered by the \
277 multi-object solver (constraints plan task C17 follow-up)"
278 ),
279 BuildError::NotRand { name } => {
280 write!(f, "`{name}` is not a `rand`/`randc` variable")
281 }
282 BuildError::Unsupported { construct, task } => write!(
283 f,
284 "{construct} in a constraint is not implemented yet (constraints plan {task})"
285 ),
286 BuildError::OutOfRangeSelect { what } => {
287 write!(f, "constant select out of the declared range: {what}")
288 }
289 BuildError::Missing { what } => write!(f, "sv-lang gave no {what}"),
290 BuildError::RandcTooWide { name, width, max } => write!(
291 f,
292 "`randc` variable `{name}` is {width} bits wide; this implementation cycles \
293 `randc` variables of at most {max} bits (IEEE 1800-2023 18.4.2 permits a \
294 limit of at least 8)"
295 ),
296 BuildError::BadSolveOperand { what } => write!(
297 f,
298 "invalid `solve ... before` operand: {what} (IEEE 1800-2023 18.5.9)"
299 ),
300 BuildError::BadDist { what } => {
301 write!(
302 f,
303 "invalid `dist` constraint: {what} (IEEE 1800-2023 18.5.3)"
304 )
305 }
306 BuildError::BadUnique { what } => {
307 write!(
308 f,
309 "invalid `unique` constraint: {what} (IEEE 1800-2023 18.5.4)"
310 )
311 }
312 BuildError::RealConstraint { what } => {
313 write!(f, "unsupported real-valued constraint: {what}")
314 }
315 BuildError::BadArray { what } => {
316 write!(f, "invalid array constraint: {what}")
317 }
318 BuildError::BadFunction { function, what } => write!(
319 f,
320 "function `{function}` cannot be called from a constraint: {what} \
321 (IEEE 1800-2023 18.5.11)"
322 ),
323 BuildError::BadSoft { what } => {
324 write!(
325 f,
326 "invalid soft constraint: {what} (IEEE 1800-2023 18.5.13)"
327 )
328 }
329 BuildError::MissingExternBody { name } => write!(
330 f,
331 "`extern constraint {name};` has no external constraint block \
332 (IEEE 1800-2023 18.5.1)"
333 ),
334 BuildError::Order(e) => write!(f, "{e}"),
335 BuildError::Ir(e) => write!(f, "{e}"),
336 }
337 }
338}
339
340impl std::error::Error for BuildError {}
341
342impl From<IrError> for BuildError {
343 fn from(e: IrError) -> Self {
344 BuildError::Ir(e)
345 }
346}
347
348impl From<OrderError> for BuildError {
349 fn from(e: OrderError) -> Self {
350 BuildError::Order(e)
351 }
352}
353
354fn unsupported(construct: impl Into<String>, task: &'static str) -> BuildError {
355 BuildError::Unsupported {
356 construct: construct.into(),
357 task,
358 }
359}
360
361#[derive(Clone, Debug, PartialEq, Eq)]
363pub struct VarInfo {
364 pub symbol: SymbolId,
366 pub name: String,
368 pub ty: Ty,
370 pub enum_domain: Option<Vec<BigUint>>,
372 pub randc: bool,
376 pub path: Vec<Step>,
381 pub window: Option<Window>,
384}
385
386#[derive(Clone, Debug, PartialEq, Eq)]
388pub struct StateHop {
389 pub symbol: SymbolId,
391 pub slot: FieldSlot,
394}
395
396#[derive(Clone, Debug, PartialEq, Eq)]
398pub struct StateInfo {
399 pub symbol: SymbolId,
403 pub name: String,
405 pub ty: Ty,
407 pub via: Vec<StateHop>,
413 pub window: Option<Window>,
417 pub root_bit: u32,
421 pub class_of: bool,
426}
427
428#[derive(Clone, Debug, PartialEq, Eq)]
431pub struct CaptureInfo {
432 pub state: u32,
434 pub symbol: SymbolId,
436 pub hops: Vec<SymbolId>,
440 pub name: String,
442 pub ty: Ty,
444}
445
446pub type LocalClassifier<'t> = Box<dyn for<'s> Fn(Expression<'s>) -> bool + 't>;
450
451#[derive(Clone, Debug, PartialEq, Eq, Default)]
454pub struct ConstraintSet {
455 pub vars: Vec<VarInfo>,
457 pub states: Vec<StateInfo>,
459 pub conjuncts: Vec<Expr>,
462 pub domain_conjunct: Vec<Option<usize>>,
468 pub before: Vec<(VarId, VarId)>,
471 pub dists: Vec<Dist>,
476 pub real_vars: Vec<RealVarInfo>,
479 pub real: RealProblem,
482 pub arrays: Vec<ArrayInfo>,
485 pub funcs: Vec<Func>,
487 pub softs: Vec<Soft>,
491 pub captures: Vec<CaptureInfo>,
494 pub blocks: Vec<BlockInfo>,
498 pub disables: usize,
501 pub disable_keys: Vec<SoftKey>,
505}
506
507#[derive(Clone, Debug, PartialEq, Eq, Default)]
510pub struct BlockInfo {
511 pub conjuncts: std::ops::Range<usize>,
516 pub items: BlockItems,
520}
521
522pub struct Builder<'t> {
524 trees: &'t [SyntaxTree],
525 vars: Vec<VarInfo>,
526 var_ix: HashMap<SymbolId, u32>,
527 pattern_binds: HashMap<SymbolId, Expr>,
530 elem_ix: HashMap<(SymbolId, Vec<Step>), u32>,
532 forced_roots: HashSet<SymbolId>,
535 real_vars: Vec<RealVarInfo>,
536 real_var_ix: HashMap<SymbolId, u32>,
537 real_leaf_ix: HashMap<(SymbolId, Vec<Step>), u32>,
539 leaf_state_ix: HashMap<(SymbolId, Vec<Step>), u32>,
541 real_conds: Vec<RealCond>,
542 real_dists: Vec<RealDist>,
543 real_before: Vec<(u32, u32)>,
544 states: Vec<StateInfo>,
545 state_ix: HashMap<(Vec<SymbolId>, u32), u32>,
547 slot_of: Option<SlotResolver<'t>>,
548 virtual_impl: Option<VirtualResolver<'t>>,
551 virtual_targets: Option<VirtualTargets<'t>>,
554 conjuncts: Vec<Expr>,
555 before: Vec<(VarId, VarId)>,
556 dists: Vec<Dist>,
557 arrays: Vec<ArrayInfo>,
559 array_ix: HashMap<SymbolId, u32>,
560 loops: Vec<LoopScope>,
562 next_loop: u32,
563 no_arrays: Option<&'static str>,
566 funcs: Vec<Func>,
568 func_ix: HashMap<(SymbolId, Vec<SymbolId>), u32>,
569 fn_base: Option<funcs::FnBase>,
573 frame: Option<funcs::Frame>,
575 softs: Vec<Soft>,
577 disables: Vec<Disable>,
578 scope: (u32, u32),
581 seq: u32,
582 guard_depth: u32,
584 local_of: Option<LocalClassifier<'t>>,
587 captures: Vec<CaptureInfo>,
588 capture_ix: HashMap<Vec<SymbolId>, usize>,
589 blocks: Vec<BlockInfo>,
591}
592
593type VirtualTargets<'t> =
598 Box<dyn for<'s> Fn(Symbol<'s>, Symbol<'s>) -> Vec<(u32, Symbol<'t>)> + 't>;
599type VirtualResolver<'t> = Box<dyn for<'s> Fn(Symbol<'s>) -> Option<Symbol<'t>> + 't>;
600type SlotResolver<'t> = Box<dyn for<'s> Fn(Symbol<'s>) -> Result<FieldSlot, String> + 't>;
601
602#[derive(Clone, Copy, Debug)]
604enum SolveOperand {
605 Int(VarId),
606 Real(u32),
607 Size,
609}
610
611const HANDLE_TY: Ty = Ty::unsigned(64);
614
615#[derive(Clone, Copy, Debug, PartialEq, Eq)]
617enum RangeOp {
618 Span,
620 Abs,
622 Pct,
624}
625
626struct Bounds {
629 lo: Option<(Ty, Expr)>,
630 hi: Option<(Ty, Expr)>,
631}
632
633fn common_ty(a: Ty, b: Ty) -> Ty {
635 Ty {
636 width: a.width.max(b.width),
637 signed: a.signed && b.signed,
638 }
639}
640
641fn is_handle_type(t: Option<Type<'_>>) -> bool {
643 t.is_some_and(|t| {
644 let c = t.canonical();
645 c.is_class() || c.is_null()
646 })
647}
648
649fn ty_of(t: Option<Type<'_>>, what: &str) -> Result<Ty, BuildError> {
650 let t = t.ok_or_else(|| BuildError::Missing {
651 what: format!("type for {what}"),
652 })?;
653 if t.is_virtual_interface() {
654 return Err(BuildError::NonIntegral {
655 what: format!("{what} of virtual interface type"),
656 });
657 }
658 if !t.is_integral() {
659 return Err(BuildError::NonIntegral {
660 what: format!("{what} of type `{}`", t.to_sv_string()),
661 });
662 }
663 let width = u32::try_from(t.bit_width()).map_err(|_| BuildError::Missing {
664 what: format!("representable width for {what}"),
665 })?;
666 if width == 0 {
667 return Err(BuildError::NonIntegral {
668 what: format!("zero-width {what}"),
669 });
670 }
671 Ok(Ty {
672 width,
673 signed: t.is_signed(),
674 })
675}
676
677fn iconst(ty: Ty, v: i64) -> Expr {
680 let m = BigInt::one() << ty.width;
681 let r = ((BigInt::from(v) % &m) + &m) % &m;
682 Expr::Const(ty, r.to_biguint().expect("reduced into [0, 2^w)"))
683}
684
685fn svint_to_biguint(v: &SVInt, ty: Ty) -> Result<BigUint, BuildError> {
686 if v.has_unknown() {
687 return Err(BuildError::FourStateLiteral {
688 text: format!("{v:?}"),
689 });
690 }
691 let w = v.bit_width();
692 let bits = v.bits();
693 let mut acc = BigUint::zero();
694 for (i, b) in bits.iter().enumerate() {
695 if *b == Bit::One {
696 acc.set_bit(i as u64, true);
697 }
698 }
699 if ty.width > w && v.is_signed() && bits.last() == Some(&Bit::One) {
700 acc |= mask(ty.width) ^ mask(w);
701 }
702 Ok(acc & mask(ty.width))
703}
704
705fn coerce(x: Expr, to: Ty) -> Expr {
708 let from = x.ty();
709 if from == to {
710 return x;
711 }
712 if !to.signed && from.signed && to.width > from.width {
713 let unsigned = Expr::Resize(Box::new(x), Ty::unsigned(from.width));
714 return Expr::Resize(Box::new(unsigned), to);
715 }
716 Expr::Resize(Box::new(x), to)
717}
718
719fn to_bool(x: Expr) -> Expr {
720 if x.ty() == Ty::BIT {
721 return x;
722 }
723 let zero = Expr::Const(x.ty(), BigUint::zero());
724 Expr::Binary(BinOp::Ne, Box::new(x), Box::new(zero), Ty::BIT)
725}
726
727fn and_all(mut parts: Vec<Expr>) -> Expr {
728 if parts.is_empty() {
729 return Expr::Const(Ty::BIT, BigUint::one());
730 }
731 let first = parts.remove(0);
732 parts.into_iter().fold(to_bool(first), |acc, p| {
733 Expr::Binary(BinOp::LogAnd, Box::new(acc), Box::new(to_bool(p)), Ty::BIT)
734 })
735}
736
737fn power(base: Expr, exp: Expr, ty: Ty) -> Expr {
744 let one = || Expr::Const(ty, BigUint::from(1u8));
745 let ew = exp.ty().width;
746 let mag_bits = if exp.ty().signed { ew - 1 } else { ew };
747 let bit = |i: u32| Expr::Slice(Box::new(exp.clone()), i, Ty::BIT);
748 let mut acc = one();
749 let mut sq = base.clone();
750 for i in 0..mag_bits {
751 acc = Expr::Ite(
752 Box::new(bit(i)),
753 Box::new(bin(BinOp::Mul, acc.clone(), sq.clone(), ty)),
754 Box::new(acc),
755 ty,
756 );
757 if i + 1 < mag_bits {
758 sq = bin(BinOp::Mul, sq.clone(), sq, ty);
759 }
760 }
761 if !exp.ty().signed {
762 return acc;
763 }
764 let is = |v: Expr| bin(BinOp::Eq, base.clone(), v, Ty::BIT);
765 let mut neg = Expr::Const(ty, BigUint::default());
766 if ty.signed {
767 let minus_one = Expr::Const(ty, (BigUint::from(1u8) << ty.width) - 1u8);
768 let alt = Expr::Ite(
769 Box::new(bit(0)),
770 Box::new(minus_one.clone()),
771 Box::new(one()),
772 ty,
773 );
774 neg = Expr::Ite(Box::new(is(minus_one)), Box::new(alt), Box::new(neg), ty);
775 }
776 neg = Expr::Ite(Box::new(is(one())), Box::new(one()), Box::new(neg), ty);
777 Expr::Ite(Box::new(bit(ew - 1)), Box::new(neg), Box::new(acc), ty)
778}
779
780fn bin(op: BinOp, l: Expr, r: Expr, ty: Ty) -> Expr {
781 Expr::Binary(op, Box::new(l), Box::new(r), ty)
782}
783
784struct Geom {
786 left: i64,
787 right: i64,
788 ew: u32,
789}
790
791impl Geom {
792 fn descending(&self) -> bool {
793 self.left >= self.right
794 }
795 fn elems(&self) -> i64 {
796 (self.left - self.right).abs() + 1
797 }
798 fn pos(&self, i: i64) -> i64 {
800 if self.descending() {
801 i - self.right
802 } else {
803 self.right - i
804 }
805 }
806 fn of(ty: Type<'_>, width: u32, what: &str) -> Result<Geom, BuildError> {
807 if ty.is_packed_array() {
808 let r = ty.packed_array_range();
809 let ew = ty
810 .element_type()
811 .map_or(1, |e| u32::try_from(e.bit_width()).unwrap_or(1).max(1));
812 if u64::from(r.width()) * u64::from(ew) == u64::from(width) {
813 return Ok(Geom {
814 left: i64::from(r.left),
815 right: i64::from(r.right),
816 ew,
817 });
818 }
819 } else if ty.is_integral() && !ty.is_struct() {
820 let r = ty.bit_vector_range();
821 if r.width() == width {
822 return Ok(Geom {
823 left: i64::from(r.left),
824 right: i64::from(r.right),
825 ew: 1,
826 });
827 }
828 }
829 Err(unsupported(
830 format!("a select into {what} of type `{}`", ty.to_sv_string()),
831 "C11",
832 ))
833 }
834}
835
836pub fn handle_path_uids(e: Expression<'_>) -> Option<Vec<SymbolId>> {
839 Builder::handle_path(e)
840 .ok()
841 .map(|p| p.iter().map(|s| s.uid()).collect())
842}
843
844impl<'t> Builder<'t> {
845 pub fn new(trees: &'t [SyntaxTree]) -> Self {
848 Builder {
849 trees,
850 vars: Vec::new(),
851 var_ix: HashMap::new(),
852 pattern_binds: HashMap::new(),
853 elem_ix: HashMap::new(),
854 forced_roots: HashSet::new(),
855 real_vars: Vec::new(),
856 real_var_ix: HashMap::new(),
857 real_leaf_ix: HashMap::new(),
858 leaf_state_ix: HashMap::new(),
859 real_conds: Vec::new(),
860 real_dists: Vec::new(),
861 real_before: Vec::new(),
862 states: Vec::new(),
863 state_ix: HashMap::new(),
864 slot_of: None,
865 virtual_impl: None,
866 virtual_targets: None,
867 conjuncts: Vec::new(),
868 before: Vec::new(),
869 dists: Vec::new(),
870 arrays: Vec::new(),
871 array_ix: HashMap::new(),
872 loops: Vec::new(),
873 next_loop: 0,
874 no_arrays: None,
875 funcs: Vec::new(),
876 func_ix: HashMap::new(),
877 fn_base: None,
878 frame: None,
879 softs: Vec::new(),
880 disables: Vec::new(),
881 scope: (0, 0),
882 seq: 0,
883 guard_depth: 0,
884 local_of: None,
885 captures: Vec::new(),
886 capture_ix: HashMap::new(),
887 blocks: Vec::new(),
888 }
889 }
890
891 pub fn set_local_classifier(&mut self, classify: impl for<'s> Fn(Expression<'s>) -> bool + 't) {
902 self.local_of = Some(Box::new(classify));
903 }
904
905 fn capture_ref(&mut self, sym: Symbol<'_>, ty: Ty) -> Expr {
908 self.capture_path(sym, &[], ty)
909 }
910
911 fn capture_path(&mut self, sym: Symbol<'_>, hops: &[Symbol<'_>], ty: Ty) -> Expr {
914 let uid = sym.uid();
915 let key: Vec<SymbolId> = std::iter::once(uid.clone())
916 .chain(hops.iter().map(|h| h.uid()))
917 .collect();
918 if let Some(&ix) = self.capture_ix.get(&key) {
919 let state = self.captures[ix].state;
920 return Expr::State(state, self.states[state as usize].ty);
921 }
922 let name = hops
923 .iter()
924 .fold(sym.name().to_string(), |n, h| format!("{n}.{}", h.name()));
925 let state = self.states.len() as u32;
926 self.states.push(StateInfo {
927 root_bit: 0,
928 symbol: uid.clone(),
929 name: name.clone(),
930 ty,
931 via: Vec::new(),
932 window: None,
933 class_of: false,
934 });
935 self.capture_ix.insert(key, self.captures.len());
936 self.captures.push(CaptureInfo {
937 state,
938 symbol: uid,
939 hops: hops.iter().map(|h| h.uid()).collect(),
940 name,
941 ty,
942 });
943 Expr::State(state, ty)
944 }
945
946 pub fn set_priority_scope(&mut self, object: u32, level: u32) {
953 self.scope = (object, level);
954 }
955
956 fn next_priority(&mut self) -> Priority {
957 let p = Priority {
958 object: self.scope.0,
959 level: self.scope.1,
960 seq: self.seq,
961 };
962 self.seq += 1;
963 p
964 }
965
966 fn make_soft(&mut self, body: Expr, dists: std::ops::Range<usize>) -> Result<Expr, BuildError> {
969 if self.in_runtime_loop() || self.frame.is_some() {
970 return Err(BuildError::BadSoft {
971 what: "a `soft` constraint inside a `foreach` over a dynamic array or queue is \
972 not supported: it would be one soft constraint per run-time element"
973 .into(),
974 });
975 }
976 let dist_refs: Vec<&Dist> = self.dists[dists.clone()].iter().collect();
977 let keys = priority::keys_of(&body, &dist_refs);
978 for k in &keys {
979 if let SoftKey::Var(v) = k
980 && self.vars[*v as usize].randc
981 {
982 return Err(BuildError::BadSoft {
983 what: format!(
984 "a `soft` constraint on the `randc` variable `{}` (soft constraints \
985 apply to `rand` variables only)",
986 self.vars[*v as usize].name
987 ),
988 });
989 }
990 }
991 let id = self.softs.len() as u32;
992 let priority = self.next_priority();
993 self.softs.push(Soft {
994 priority,
995 dists: dists.map(|d| d as u32).collect(),
996 keys,
997 disabled: false,
998 disabled_by: Vec::new(),
999 });
1000 Ok(priority::wrap_soft(id, body))
1001 }
1002
1003 fn disable_soft(&mut self, c: Constraint<'_>) -> Result<(), BuildError> {
1005 if self.guard_depth > 0 || self.in_runtime_loop() {
1006 return Err(BuildError::BadSoft {
1007 what: "a `disable soft` inside an implication, an `if` or a `foreach` over a \
1008 dynamic array or queue is not supported (it discards unconditionally)"
1009 .into(),
1010 });
1011 }
1012 let target = c
1013 .children()
1014 .into_iter()
1015 .find_map(|n| n.as_expression())
1016 .ok_or_else(|| BuildError::Missing {
1017 what: "target expression of `disable soft`".into(),
1018 })?;
1019 let key = if let Some(arr) = Self::size_call_array(target) {
1020 SoftKey::ArraySize(self.array_of(arr)?)
1021 } else if let Some(chain) = aggregate::aggregate_chain(target, &self.forced_roots)
1022 && !chain.steps.is_empty()
1023 {
1024 let (leaf, declared) = self.declared_leaf(chain, "the target of `disable soft`")?;
1025 match declared {
1026 Some(Declared::Int(id)) => SoftKey::Var(id),
1027 Some(Declared::Real(_)) => {
1028 return Err(BuildError::BadSoft {
1029 what: "`disable soft` of a real variable is not supported (soft \
1030 constraints are relaxed over integral variables only)"
1031 .into(),
1032 });
1033 }
1034 None => {
1035 return Err(BuildError::BadSoft {
1036 what: format!(
1037 "`disable soft` names `{}`, which is not a `rand` variable of this \
1038 class",
1039 leaf.name
1040 ),
1041 });
1042 }
1043 }
1044 } else {
1045 let sym = match target.kind() {
1046 ExpressionKind::NamedValue => target.referenced_symbol(),
1047 ExpressionKind::MemberAccess => {
1048 let base = Self::operand(target, "a member access")?;
1049 let is_this = base.kind() == ExpressionKind::NamedValue
1050 && base.referenced_symbol().is_some_and(|s| s.name() == "this");
1051 if !is_this {
1052 return Err(BuildError::ForeignObjectAccess {
1053 what: "the target of `disable soft`".into(),
1054 });
1055 }
1056 target.member_symbol()
1057 }
1058 other => {
1059 return Err(BuildError::BadSoft {
1060 what: format!(
1061 "a `{other:?}` expression as the target of `disable soft` (name a \
1062 whole variable, `this.v` or `A.size`)"
1063 ),
1064 });
1065 }
1066 };
1067 let sym = sym.ok_or_else(|| BuildError::Missing {
1068 what: "symbol of the target of `disable soft`".into(),
1069 })?;
1070 let uid = sym.uid();
1071 if let Some(&id) = self.var_ix.get(&uid) {
1072 SoftKey::Var(id)
1073 } else if self.array_ix.contains_key(&uid) {
1074 SoftKey::Array(self.array_of(target)?)
1075 } else if self.real_var_ix.contains_key(&uid) {
1076 return Err(BuildError::BadSoft {
1077 what: "`disable soft` of a real variable is not supported (soft constraints \
1078 are relaxed over integral variables only)"
1079 .into(),
1080 });
1081 } else {
1082 return Err(BuildError::BadSoft {
1083 what: format!(
1084 "`disable soft` names `{}`, which is not a `rand` variable of this class",
1085 sym.name()
1086 ),
1087 });
1088 }
1089 };
1090 let priority = self.next_priority();
1091 self.disables.push(Disable { priority, key });
1092 Ok(())
1093 }
1094
1095 pub fn set_slot_resolver(
1100 &mut self,
1101 resolver: impl for<'s> Fn(Symbol<'s>) -> Result<FieldSlot, String> + 't,
1102 ) {
1103 self.slot_of = Some(Box::new(resolver));
1104 }
1105
1106 pub fn set_virtual_resolver(
1113 &mut self,
1114 resolver: impl for<'s> Fn(Symbol<'s>) -> Option<Symbol<'t>> + 't,
1115 ) {
1116 self.virtual_impl = Some(Box::new(resolver));
1117 }
1118
1119 pub fn set_virtual_targets(
1125 &mut self,
1126 resolver: impl for<'s> Fn(Symbol<'s>, Symbol<'s>) -> Vec<(u32, Symbol<'t>)> + 't,
1127 ) {
1128 self.virtual_targets = Some(Box::new(resolver));
1129 }
1130
1131 pub fn declare_rand(&mut self, sym: Symbol<'_>) -> Result<u32, BuildError> {
1134 let uid = sym.uid();
1135 if let Some(&id) = self.var_ix.get(&uid) {
1136 return Ok(id);
1137 }
1138 let name = sym.name().to_string();
1139 let randc = match sym.rand_mode() {
1140 RandMode::None => return Err(BuildError::NotRand { name }),
1141 RandMode::RandC => true,
1142 RandMode::Rand => false,
1143 };
1144 let vt = sym.value_type().ok_or_else(|| BuildError::Missing {
1145 what: format!("value type for `{name}`"),
1146 })?;
1147 let id = self.new_rand_var(uid.clone(), name, vt, randc, Vec::new(), None)?;
1148 self.var_ix.insert(uid, id);
1149 Ok(id)
1150 }
1151
1152 pub fn declare_var_forced(&mut self, sym: Symbol<'_>) -> Result<u32, BuildError> {
1159 let uid = sym.uid();
1160 if let Some(&id) = self.var_ix.get(&uid) {
1161 return Ok(id);
1162 }
1163 let name = sym.name().to_string();
1164 let randc = matches!(sym.rand_mode(), RandMode::RandC);
1165 let vt = sym.value_type().ok_or_else(|| BuildError::Missing {
1166 what: format!("value type for `{name}`"),
1167 })?;
1168 let id = self.new_rand_var(uid.clone(), name, vt, randc, Vec::new(), None)?;
1169 self.var_ix.insert(uid, id);
1170 Ok(id)
1171 }
1172
1173 fn enum_values(vt: Type<'_>, ty: Ty) -> Result<Option<Vec<BigUint>>, BuildError> {
1176 if !vt.is_enum() {
1177 return Ok(None);
1178 }
1179 let mut vals = Vec::new();
1180 for m in vt.canonical().enum_members() {
1181 let cv = m.constant_value().ok_or_else(|| BuildError::Missing {
1182 what: format!("value of enum member `{}`", m.name()),
1183 })?;
1184 let i = cv.as_integer().ok_or_else(|| BuildError::Missing {
1185 what: format!("integer value of enum member `{}`", m.name()),
1186 })?;
1187 vals.push(svint_to_biguint(i, ty)?);
1188 }
1189 Ok(Some(vals))
1190 }
1191
1192 fn new_rand_var(
1196 &mut self,
1197 uid: SymbolId,
1198 name: String,
1199 vt: Type<'_>,
1200 randc: bool,
1201 path: Vec<Step>,
1202 window: Option<Window>,
1203 ) -> Result<u32, BuildError> {
1204 if vt.is_virtual_interface() {
1205 return Err(BuildError::VirtualInterfaceRand { name });
1206 }
1207 let ty = ty_of(Some(vt), &format!("`rand` variable `{name}`"))?;
1208 if randc && ty.width > MAX_RANDC_WIDTH {
1209 return Err(BuildError::RandcTooWide {
1210 name,
1211 width: ty.width,
1212 max: MAX_RANDC_WIDTH,
1213 });
1214 }
1215 let enum_domain = Self::enum_values(vt, ty)?;
1216 let id = self.vars.len() as u32;
1217 self.vars.push(VarInfo {
1218 symbol: uid,
1219 name,
1220 ty,
1221 enum_domain,
1222 randc,
1223 path,
1224 window,
1225 });
1226 Ok(id)
1227 }
1228
1229 pub fn add_block(&mut self, block: Symbol<'_>) -> Result<(), BuildError> {
1231 let start = self.conjuncts.len();
1232 let from = [
1233 self.dists.len(),
1234 self.softs.len(),
1235 self.before.len(),
1236 self.real_conds.len(),
1237 self.real_dists.len(),
1238 self.real_before.len(),
1239 self.disables.len(),
1240 ];
1241 self.add_block_body(block)?;
1242 self.blocks.push(BlockInfo {
1243 conjuncts: start..self.conjuncts.len(),
1244 items: BlockItems {
1245 dists: from[0]..self.dists.len(),
1246 softs: from[1]..self.softs.len(),
1247 before: from[2]..self.before.len(),
1248 real_conds: from[3]..self.real_conds.len(),
1249 real_dists: from[4]..self.real_dists.len(),
1250 real_before: from[5]..self.real_before.len(),
1251 disables: from[6]..self.disables.len(),
1252 },
1253 });
1254 Ok(())
1255 }
1256
1257 fn add_block_body(&mut self, block: Symbol<'_>) -> Result<(), BuildError> {
1258 let tree = block
1259 .constraint_block_constraints()
1260 .ok_or_else(|| BuildError::Missing {
1261 what: format!("bound body for constraint block `{}`", block.name()),
1262 })?;
1263 let c = tree.as_constraint().ok_or_else(|| BuildError::Missing {
1264 what: format!("constraint body of block `{}`", block.name()),
1265 })?;
1266 let flags = block.constraint_block_flags();
1273 if c.kind() == ConstraintKind::Invalid
1274 && flags.contains(ConstraintBlockFlags::EXTERN)
1275 && !flags.contains(ConstraintBlockFlags::PURE)
1276 {
1277 if flags.contains(ConstraintBlockFlags::EXPLICIT_EXTERN) {
1278 return Err(BuildError::MissingExternBody {
1279 name: block.name().to_string(),
1280 });
1281 }
1282 return Ok(());
1283 }
1284 self.add_constraint(c)
1285 }
1286
1287 pub fn add_constraint(&mut self, c: Constraint<'_>) -> Result<(), BuildError> {
1289 let mut out = self.constraint(c)?;
1290 self.conjuncts.append(&mut out);
1291 Ok(())
1292 }
1293
1294 pub fn finish(mut self) -> Result<ConstraintSet, BuildError> {
1296 let mut domain_conjunct = vec![None; self.vars.len()];
1297 for (i, v) in self.vars.iter().enumerate() {
1298 let Some(domain) = &v.enum_domain else {
1299 continue;
1300 };
1301 let var = Expr::Var(i as u32, v.ty);
1302 let mut any: Option<Expr> = None;
1303 for val in domain {
1304 let eq = bin(
1305 BinOp::Eq,
1306 var.clone(),
1307 Expr::Const(v.ty, val.clone()),
1308 Ty::BIT,
1309 );
1310 any = Some(match any {
1311 None => eq,
1312 Some(prev) => bin(BinOp::LogOr, prev, eq, Ty::BIT),
1313 });
1314 }
1315 if let Some(e) = any {
1316 domain_conjunct[i] = Some(self.conjuncts.len());
1317 self.conjuncts.push(e);
1318 }
1319 }
1320 for c in &self.conjuncts {
1321 c.validate()?;
1322 }
1323 for d in &self.dists {
1324 d.validate()?;
1325 }
1326 order::levels(self.vars.len(), &self.before)?;
1327 let (real_vars, real) = self.finish_real()?;
1328 priority::apply_disables(&mut self.softs, &self.disables);
1329 Ok(ConstraintSet {
1330 vars: self.vars,
1331 states: self.states,
1332 conjuncts: self.conjuncts,
1333 domain_conjunct,
1334 before: self.before,
1335 dists: self.dists,
1336 real_vars,
1337 real,
1338 arrays: self.arrays,
1339 funcs: self.funcs,
1340 softs: self.softs,
1341 captures: self.captures,
1342 blocks: self.blocks,
1343 disables: self.disables.len(),
1344 disable_keys: self.disables.iter().map(|d| d.key).collect(),
1345 })
1346 }
1347
1348 fn solve_side_len(&self, c: Constraint<'_>) -> Result<usize, BuildError> {
1352 for tree in self.trees {
1353 if let Some(node) = c.syntax(tree) {
1354 let s =
1355 SolveBeforeConstraintSyntax::cast(node).ok_or_else(|| BuildError::Missing {
1356 what: "`solve ... before` syntax for a SolveBefore constraint".into(),
1357 })?;
1358 return Ok(s.before_expr().iter().count());
1359 }
1360 }
1361 Err(BuildError::Missing {
1362 what: "syntax node for a constraint (is its syntax tree passed to the builder?)".into(),
1363 })
1364 }
1365
1366 fn solve_operand(&mut self, e: Expression<'_>) -> Result<SolveOperand, BuildError> {
1370 if let Some(chain) = aggregate::aggregate_chain(e, &self.forced_roots)
1373 && !chain.steps.is_empty()
1374 {
1375 let (leaf, declared) = self.declared_leaf(chain, "a `solve ... before` operand")?;
1376 if leaf.randc() {
1377 return Err(BuildError::BadSolveOperand {
1378 what: format!(
1379 "`{}` is `randc`, which is always solved before every `rand` variable",
1380 leaf.name
1381 ),
1382 });
1383 }
1384 return match declared {
1385 Some(Declared::Int(id)) => Ok(SolveOperand::Int(id)),
1386 Some(Declared::Real(id)) => Ok(SolveOperand::Real(id)),
1387 None => Err(BuildError::BadSolveOperand {
1388 what: format!("`{}` is not a `rand` variable of this class", leaf.name),
1389 }),
1390 };
1391 }
1392 let sym = match e.kind() {
1393 ExpressionKind::NamedValue => e.referenced_symbol(),
1394 ExpressionKind::MemberAccess => {
1395 let base = Self::operand(e, "a member access")?;
1396 let is_this = base.kind() == ExpressionKind::NamedValue
1397 && base.referenced_symbol().is_some_and(|s| s.name() == "this");
1398 if !is_this {
1399 return Err(BuildError::ForeignObjectAccess {
1400 what: format!(
1401 "`solve ... before` operand `.{}`",
1402 e.member_symbol()
1403 .map_or(String::new(), |m| m.name().to_string())
1404 ),
1405 });
1406 }
1407 e.member_symbol()
1408 }
1409 ExpressionKind::Call => {
1410 let Some(arr) = Self::size_call_array(e) else {
1411 return Err(unsupported(
1412 "a call other than `array.size` as a `solve ... before` operand",
1413 "C11",
1414 ));
1415 };
1416 self.arrays_allowed("an array size")?;
1417 self.array_of(arr)?;
1418 return Ok(SolveOperand::Size);
1419 }
1420 other => {
1421 return Err(unsupported(
1422 format!("a `{other:?}` expression as a `solve ... before` operand"),
1423 "C11",
1424 ));
1425 }
1426 };
1427 let sym = sym.ok_or_else(|| BuildError::Missing {
1428 what: "symbol of a `solve ... before` operand".into(),
1429 })?;
1430 let name = sym.name().to_string();
1431 if sym.rand_mode() == RandMode::RandC {
1432 return Err(BuildError::BadSolveOperand {
1433 what: format!(
1434 "`{name}` is `randc`, which is always solved before every `rand` variable"
1435 ),
1436 });
1437 }
1438 let uid = sym.uid();
1439 if let Some(&id) = self.var_ix.get(&uid) {
1440 return Ok(SolveOperand::Int(id));
1441 }
1442 match self.real_var_ix.get(&uid) {
1443 Some(&id) => Ok(SolveOperand::Real(id)),
1444 None => Err(BuildError::BadSolveOperand {
1445 what: format!("`{name}` is not a `rand` variable of this class"),
1446 }),
1447 }
1448 }
1449
1450 fn is_soft(&self, c: Constraint<'_>) -> Result<bool, BuildError> {
1451 for tree in self.trees {
1452 if let Some(node) = c.syntax(tree) {
1453 return Ok(
1454 ExpressionConstraintSyntax::cast(node).is_some_and(|s| s.soft().is_some())
1455 );
1456 }
1457 }
1458 Err(BuildError::Missing {
1459 what: "syntax node for a constraint (is its syntax tree passed to the builder?)".into(),
1460 })
1461 }
1462
1463 fn constraint(&mut self, c: Constraint<'_>) -> Result<Vec<Expr>, BuildError> {
1464 fn kids<'d>(c: Constraint<'d>) -> Vec<Constraint<'d>> {
1465 c.children()
1466 .into_iter()
1467 .filter_map(|n| n.as_constraint())
1468 .collect()
1469 }
1470 match c.kind() {
1471 ConstraintKind::List => {
1472 let mut out = Vec::new();
1473 for k in kids(c) {
1474 out.extend(self.constraint(k)?);
1475 }
1476 Ok(out)
1477 }
1478 ConstraintKind::Expression => {
1479 let soft = self.is_soft(c)?;
1480 let e = c.expr().ok_or_else(|| BuildError::Missing {
1481 what: "expression of an expression constraint".into(),
1482 })?;
1483 if soft
1484 && (real::reads_real(e)
1485 || (e.kind() == ExpressionKind::Dist && real::dist_is_real(e)))
1486 {
1487 return Err(BuildError::BadSoft {
1488 what: "a `soft` constraint over a real value is not supported (soft \
1489 constraints are relaxed over integral variables only)"
1490 .into(),
1491 });
1492 }
1493 let dists_from = self.dists.len();
1494 let built = if e.kind() == ExpressionKind::Dist {
1495 if real::dist_is_real(e) {
1496 self.real_dist(e)?;
1497 return Ok(Vec::new());
1498 }
1499 self.dist(e)?
1500 } else {
1501 if real::reads_real(e) {
1502 let cond = self.real_cond(e)?;
1503 self.real_conds.push(cond);
1504 return Ok(Vec::new());
1505 }
1506 to_bool(self.expr(e)?)
1507 };
1508 if soft {
1509 let dists = dists_from..self.dists.len();
1510 return Ok(vec![self.make_soft(built, dists)?]);
1511 }
1512 Ok(vec![built])
1513 }
1514 ConstraintKind::Implication | ConstraintKind::Conditional
1517 if real::constraint_reads_real(c) =>
1518 {
1519 let cond = self.real_constraint(c)?;
1520 self.real_conds.push(cond);
1521 Ok(Vec::new())
1522 }
1523 ConstraintKind::Implication => {
1524 let p = c.predicate().ok_or_else(|| BuildError::Missing {
1525 what: "predicate of an implication".into(),
1526 })?;
1527 let pred = to_bool(self.expr(p)?);
1528 let mut body = Vec::new();
1529 self.guard_depth += 1;
1530 for k in kids(c) {
1531 match self.constraint(k) {
1532 Ok(b) => body.extend(b),
1533 Err(e) => {
1534 self.guard_depth -= 1;
1535 return Err(e);
1536 }
1537 }
1538 }
1539 self.guard_depth -= 1;
1540 Ok(vec![Expr::Guard(
1543 Box::new(pred),
1544 Box::new(and_all(body)),
1545 Box::new(Expr::Const(Ty::BIT, BigUint::one())),
1546 Ty::BIT,
1547 )])
1548 }
1549 ConstraintKind::Conditional => {
1550 let p = c.predicate().ok_or_else(|| BuildError::Missing {
1551 what: "predicate of an if constraint".into(),
1552 })?;
1553 let pred = to_bool(self.expr(p)?);
1554 let arms = kids(c);
1555 if arms.is_empty() || arms.len() > 2 {
1556 return Err(BuildError::Missing {
1557 what: format!("one or two arms of an if constraint (got {})", arms.len()),
1558 });
1559 }
1560 self.guard_depth += 1;
1561 let arms_built = arms
1562 .iter()
1563 .map(|a| self.constraint(*a).map(and_all))
1564 .collect::<Result<Vec<_>, _>>();
1565 self.guard_depth -= 1;
1566 let mut arms_built = arms_built?.into_iter();
1567 let then_c = arms_built.next().ok_or_else(|| BuildError::Missing {
1568 what: "the then arm of an if constraint".into(),
1569 })?;
1570 let else_c = arms_built
1571 .next()
1572 .unwrap_or_else(|| Expr::Const(Ty::BIT, BigUint::one()));
1573 Ok(vec![Expr::Guard(
1574 Box::new(pred),
1575 Box::new(then_c),
1576 Box::new(else_c),
1577 Ty::BIT,
1578 )])
1579 }
1580 ConstraintKind::Uniqueness => self.unique(c),
1581 ConstraintKind::SolveBefore => {
1582 let split = self.solve_side_len(c)?;
1585 let mut ids = Vec::new();
1586 for k in c.children() {
1587 let e = k.as_expression().ok_or_else(|| BuildError::Missing {
1588 what: "expression operand of `solve ... before`".into(),
1589 })?;
1590 ids.push(self.solve_operand(e)?);
1591 }
1592 if split > ids.len() {
1593 return Err(BuildError::Missing {
1594 what: "operands of `solve ... before` matching its syntax".into(),
1595 });
1596 }
1597 let (first, later) = ids.split_at(split);
1598 for &a in first {
1599 for &b in later {
1600 match (a, b) {
1601 (SolveOperand::Int(a), SolveOperand::Int(b)) => {
1602 self.before.push((a, b));
1603 }
1604 (SolveOperand::Real(a), SolveOperand::Real(b)) => {
1605 self.real_before.push((a, b));
1606 }
1607 (SolveOperand::Size, SolveOperand::Int(_) | SolveOperand::Real(_)) => {}
1615 (SolveOperand::Int(_) | SolveOperand::Real(_), SolveOperand::Size)
1616 | (SolveOperand::Size, SolveOperand::Size) => {
1617 return Err(unsupported(
1618 "`solve ... before` placing a variable or another array size \
1619 before an array `.size()`",
1620 "array sizes are always solved first, as one joint sub-problem",
1621 ));
1622 }
1623 _ => {}
1627 }
1628 }
1629 }
1630 Ok(Vec::new())
1631 }
1632 ConstraintKind::Invalid => Err(BuildError::Missing {
1633 what: "well-formed constraint (the frontend diagnosed an error in it)".into(),
1634 }),
1635 ConstraintKind::DisableSoft => {
1636 self.disable_soft(c)?;
1638 Ok(Vec::new())
1639 }
1640 ConstraintKind::Foreach => self.foreach(c),
1641 other => Err(unsupported(format!("constraint kind {other:?}"), "C2")),
1642 }
1643 }
1644
1645 fn symbol_ref(&mut self, sym: Symbol<'_>, ty: Ty) -> Result<Expr, BuildError> {
1646 if self.frame.is_some()
1649 && sym.kind() == SymbolKind::ClassProperty
1650 && let Some(base) = self.fn_base.clone()
1651 {
1652 return self.based_state(&base, &[sym], ty);
1653 }
1654 let uid = sym.uid();
1655 if let Some(x) = self.pattern_binds.get(&uid) {
1656 return Ok(x.clone());
1657 }
1658 if let Some(&id) = self.var_ix.get(&uid) {
1659 if self.frame.is_some() {
1660 return Err(self.rand_read_in_function(sym.name()));
1661 }
1662 return Ok(Expr::Var(id, self.vars[id as usize].ty));
1663 }
1664 self.state_ref(sym, &[], 0, ty)
1665 }
1666
1667 fn state_ref(
1670 &mut self,
1671 root: Symbol<'_>,
1672 hops: &[Symbol<'_>],
1673 root_bit: u32,
1674 ty: Ty,
1675 ) -> Result<Expr, BuildError> {
1676 let key: Vec<SymbolId> = std::iter::once(root.uid())
1677 .chain(hops.iter().map(|h| h.uid()))
1678 .collect();
1679 let key = (key, root_bit);
1680 if let Some(&id) = self.state_ix.get(&key) {
1681 return Ok(Expr::State(id, self.states[id as usize].ty));
1682 }
1683 let mut name = root.name().to_string();
1684 let mut via = Vec::with_capacity(hops.len());
1685 for h in hops {
1686 name = format!("{name}.{}", h.name());
1687 let resolver =
1688 self.slot_of
1689 .as_ref()
1690 .ok_or_else(|| BuildError::ForeignObjectAccess {
1691 what: format!(
1692 "read of `{name}` (this builder was given no object layout to place \
1693 another object's members)"
1694 ),
1695 })?;
1696 let slot = resolver(*h).map_err(|why| BuildError::Missing {
1697 what: format!("field slot of `{name}`: {why}"),
1698 })?;
1699 via.push(StateHop {
1700 symbol: h.uid(),
1701 slot,
1702 });
1703 }
1704 let id = self.states.len() as u32;
1705 self.states.push(StateInfo {
1706 symbol: root.uid(),
1707 name,
1708 ty,
1709 via,
1710 window: None,
1711 root_bit,
1712 class_of: false,
1713 });
1714 self.state_ix.insert(key, id);
1715 Ok(Expr::State(id, ty))
1716 }
1717
1718 fn local_handle_root<'d>(mut e: Expression<'d>) -> Option<Expression<'d>> {
1721 loop {
1724 let kind = e.kind();
1725 if kind == ExpressionKind::MemberAccess {
1726 e = e.left()?;
1727 } else if kind == ExpressionKind::Conversion {
1728 e = e.conversion_operand()?;
1729 } else if kind == ExpressionKind::NamedValue
1730 || kind == ExpressionKind::HierarchicalValue
1731 {
1732 return e
1733 .referenced_symbol()
1734 .is_some_and(|s| s.name() != "this")
1735 .then_some(e);
1736 } else {
1737 return None;
1738 }
1739 }
1740 }
1741
1742 fn handle_path<'d>(e: Expression<'d>) -> Result<Vec<Symbol<'d>>, BuildError> {
1745 match e.kind() {
1746 ExpressionKind::NamedValue | ExpressionKind::HierarchicalValue => {
1747 let sym = e.referenced_symbol().ok_or_else(|| BuildError::Missing {
1748 what: "symbol of a named value".into(),
1749 })?;
1750 Ok(if sym.name() == "this" {
1751 Vec::new()
1752 } else {
1753 vec![sym]
1754 })
1755 }
1756 ExpressionKind::MemberAccess => {
1757 let mut path = Self::handle_path(Self::operand(e, "a member access")?)?;
1758 path.push(e.member_symbol().ok_or_else(|| BuildError::Missing {
1759 what: "member symbol of a member access".into(),
1760 })?);
1761 Ok(path)
1762 }
1763 ExpressionKind::Conversion => {
1764 let inner = e.conversion_operand().ok_or_else(|| BuildError::Missing {
1765 what: "operand of a conversion".into(),
1766 })?;
1767 Self::handle_path(inner)
1768 }
1769 ExpressionKind::ElementSelect => {
1772 let arr = e.select_value().ok_or_else(|| BuildError::Missing {
1773 what: "array of an element select".into(),
1774 })?;
1775 match arr.kind() {
1776 ExpressionKind::NamedValue | ExpressionKind::HierarchicalValue => {
1777 Self::handle_path(arr)
1778 }
1779 other => Err(unsupported(
1780 format!(
1781 "an element of a `{other:?}` array of handles as the object of a member access"
1782 ),
1783 "C17 follow-up",
1784 )),
1785 }
1786 }
1787 other => Err(unsupported(
1788 format!("a `{other:?}` expression as the object of a member access"),
1789 "C17 follow-up",
1790 )),
1791 }
1792 }
1793
1794 fn handle_root_element(&mut self, e: Expression<'_>) -> Result<Option<(u32, u32)>, BuildError> {
1800 let mut cur = e;
1801 loop {
1804 let kind = cur.kind();
1805 if kind == ExpressionKind::MemberAccess {
1806 cur = Self::operand(cur, "a member access")?;
1807 } else if kind == ExpressionKind::Conversion {
1808 cur = cur
1809 .conversion_operand()
1810 .ok_or_else(|| BuildError::Missing {
1811 what: "operand of a conversion".into(),
1812 })?;
1813 } else if kind == ExpressionKind::ElementSelect {
1814 break;
1815 } else {
1816 return Ok(None);
1817 }
1818 }
1819 let arr = cur.select_value().ok_or_else(|| BuildError::Missing {
1820 what: "array of an element select".into(),
1821 })?;
1822 let at = arr
1823 .expr_type()
1824 .map(|t| t.canonical())
1825 .ok_or_else(|| BuildError::Missing {
1826 what: "type of an array of handles".into(),
1827 })?;
1828 if !at.is_unpacked_array() || at.fixed_unpacked_array_range().width() == 0 {
1829 return Err(unsupported(
1830 "an element of a dynamic array, queue or associative array of handles in a constraint",
1831 "C17 follow-up",
1832 ));
1833 }
1834 let sel = cur.selector().ok_or_else(|| BuildError::Missing {
1835 what: "index of an element select".into(),
1836 })?;
1837 let ix = self.expr(sel)?;
1838 let i = Self::constant_index(&ix).ok_or_else(|| {
1839 unsupported(
1840 "an element of an array of handles at a computed index",
1841 "C17 follow-up",
1842 )
1843 })?;
1844 let r = at.fixed_unpacked_array_range();
1845 let lo = i64::from(r.left).min(i64::from(r.right));
1846 let n = i64::from(r.width());
1847 if !(0..n).contains(&(i - lo)) {
1848 return Err(BuildError::OutOfRangeSelect {
1849 what: format!("handle array index {i} outside [{}:{}]", r.left, r.right),
1850 });
1851 }
1852 Ok(Some((((i - lo) * 64) as u32, (n * 64) as u32)))
1853 }
1854
1855 fn handle_state(&mut self, e: Expression<'_>, ty: Ty) -> Result<Expr, BuildError> {
1859 let path = Self::handle_path(e)?;
1860 if path.is_empty() {
1861 return Err(unsupported("a bare `this` as a value", "C17 follow-up"));
1862 }
1863 if self.frame.is_some()
1864 && let Some(base) = self.fn_base.clone()
1865 {
1866 return self.based_state(&base, &path, ty);
1869 }
1870 let Some((root, hops)) = path.split_first() else {
1871 return Err(unsupported("a bare `this` as a value", "C17 follow-up"));
1872 };
1873 if self.var_ix.contains_key(&root.uid()) {
1874 return Err(unsupported(
1875 format!("the randomized handle `{}` in a constraint", root.name()),
1876 "C17 follow-up",
1877 ));
1878 }
1879 let elem = self.handle_root_element(e)?;
1882 for (i, step) in path[..path.len() - 1].iter().enumerate() {
1883 let is_handle = (i == 0 && elem.is_some())
1884 || step.value_type().is_some_and(|t| t.canonical().is_class());
1885 if !is_handle {
1886 return Err(unsupported(
1887 format!(
1888 "a member access through `{}` (step {i}), which is not a class handle",
1889 step.name()
1890 ),
1891 "C17 follow-up",
1892 ));
1893 }
1894 }
1895 let bit = elem.map_or(0, |(b, _)| b);
1896 let out = self.state_ref(*root, hops, bit, ty)?;
1897 if let (Some((bit, whole)), true, Expr::State(id, _)) = (elem, hops.is_empty(), &out) {
1898 self.states[*id as usize].window = Some(Window {
1900 bit,
1901 width: 64,
1902 field_width: whole,
1903 });
1904 }
1905 Ok(out)
1906 }
1907
1908 fn handle_value(&mut self, e: Expression<'_>) -> Result<Expr, BuildError> {
1911 match e.kind() {
1912 ExpressionKind::NullLiteral => Ok(Expr::Const(HANDLE_TY, BigUint::zero())),
1913 ExpressionKind::Conversion => {
1916 let inner = e.conversion_operand().ok_or_else(|| BuildError::Missing {
1917 what: "operand of a conversion".into(),
1918 })?;
1919 self.handle_value(inner)
1920 }
1921 ExpressionKind::NamedValue
1922 | ExpressionKind::HierarchicalValue
1923 | ExpressionKind::ElementSelect
1924 | ExpressionKind::MemberAccess => self.handle_state(e, HANDLE_TY),
1925 other => Err(unsupported(
1926 format!("a `{other:?}` class-handle expression"),
1927 "C17 follow-up",
1928 )),
1929 }
1930 }
1931
1932 pub fn build_expr(&mut self, e: Expression<'_>) -> Result<Expr, BuildError> {
1935 let out = self.expr(e)?;
1936 out.validate()?;
1937 Ok(out)
1938 }
1939
1940 fn operand<'d>(e: Expression<'d>, what: &str) -> Result<Expression<'d>, BuildError> {
1941 e.left().ok_or_else(|| BuildError::Missing {
1942 what: format!("operand of {what}"),
1943 })
1944 }
1945
1946 fn expr(&mut self, e: Expression<'_>) -> Result<Expr, BuildError> {
1947 let kind = e.kind();
1948 if is_handle_type(e.expr_type()) {
1949 return self.handle_value(e);
1950 }
1951 let ty = ty_of(e.expr_type(), &format!("a `{kind:?}` expression"))?;
1952 let pattern_cond = kind == ExpressionKind::ConditionalOp
1956 && e.conditions().iter().any(|c| c.pattern().is_some());
1957 if !pattern_cond
1958 && let Some(cv) = e.constant_value()
1959 && let Some(i) = cv.as_integer()
1960 {
1961 return Ok(Expr::Const(ty, svint_to_biguint(i, ty)?));
1962 }
1963 match kind {
1964 ExpressionKind::NamedValue | ExpressionKind::HierarchicalValue => {
1965 let sym = e.referenced_symbol().ok_or_else(|| BuildError::Missing {
1966 what: "symbol of a named value".into(),
1967 })?;
1968 if let Some(local) = self.frame_var(sym) {
1969 return Ok(local);
1970 }
1971 if sym.kind() == SymbolKind::Iterator {
1972 self.not_in_function("an iterator variable")?;
1973 return self.loop_ref(sym, ty);
1974 }
1975 if self.local_of.as_ref().is_some_and(|c| c(e)) {
1976 self.not_in_function("a local of an inline constraint's call site")?;
1977 return Ok(self.capture_ref(sym, ty));
1978 }
1979 self.symbol_ref(sym, ty)
1980 }
1981 ExpressionKind::MemberAccess => {
1982 if let Some(chain) = aggregate::aggregate_chain(e, &self.forced_roots)
1985 && !chain.steps.is_empty()
1986 {
1987 return self.aggregate_value(chain, ty);
1988 }
1989 let base = Self::operand(e, "a member access")?;
1990 let is_this = base.kind() == ExpressionKind::NamedValue
1991 && base.referenced_symbol().is_some_and(|s| s.name() == "this");
1992 let member = e.member_symbol().ok_or_else(|| BuildError::Missing {
1993 what: "member symbol of a member access".into(),
1994 })?;
1995 if !is_this {
1996 if is_handle_type(base.expr_type()) {
1997 if let Some(root) = Self::local_handle_root(base)
2000 && self.local_of.as_ref().is_some_and(|c| c(root))
2001 {
2002 self.not_in_function("a local of an inline constraint's call site")?;
2003 let path = Self::handle_path(e)?;
2004 let (r, hops) =
2005 path.split_first().ok_or_else(|| BuildError::Missing {
2006 what: "handle path of a member read".into(),
2007 })?;
2008 return Ok(self.capture_path(*r, hops, ty));
2009 }
2010 return self.handle_state(e, ty);
2012 }
2013 let base_ty = base.expr_type();
2016 if !base_ty.is_some_and(|t| t.is_integral() && (t.is_struct() || t.is_union()))
2017 {
2018 let what = format!("member access `.{}`", member.name());
2019 return Err(unsupported(
2020 format!(
2021 "{what} on a value that is neither a class handle, a packed \
2022 struct or union, nor a member of an unpacked struct property \
2023 of this class"
2024 ),
2025 "C14",
2026 ));
2027 }
2028 let whole = self.expr(base)?;
2029 let low = u32::try_from(member.field_bit_offset()).map_err(|_| {
2030 BuildError::Missing {
2031 what: format!("bit offset of packed member `{}`", member.name()),
2032 }
2033 })?;
2034 let slice = Expr::Slice(Box::new(whole), low, Ty::unsigned(ty.width));
2035 return Ok(if slice.ty() == ty {
2036 slice
2037 } else {
2038 Expr::Resize(Box::new(slice), ty)
2039 });
2040 }
2041 self.symbol_ref(member, ty)
2042 }
2043 ExpressionKind::UnaryOp => {
2044 let op = e.unary_op().ok_or_else(|| BuildError::Missing {
2045 what: "operator of a unary expression".into(),
2046 })?;
2047 let a = self.expr(Self::operand(e, "a unary operator")?)?;
2048 let (uop, rty) = match op {
2049 UnaryOp::Plus => return Ok(a),
2050 UnaryOp::Minus => (UnOp::Neg, ty),
2051 UnaryOp::BitwiseNot => (UnOp::BitNot, ty),
2052 UnaryOp::BitwiseAnd => (UnOp::RedAnd, Ty::BIT),
2053 UnaryOp::BitwiseOr => (UnOp::RedOr, Ty::BIT),
2054 UnaryOp::BitwiseXor => (UnOp::RedXor, Ty::BIT),
2055 UnaryOp::BitwiseNand => (UnOp::RedNand, Ty::BIT),
2056 UnaryOp::BitwiseNor => (UnOp::RedNor, Ty::BIT),
2057 UnaryOp::BitwiseXnor => (UnOp::RedXnor, Ty::BIT),
2058 UnaryOp::LogicalNot => (UnOp::LogNot, Ty::BIT),
2059 other => {
2060 return Err(unsupported(
2061 format!("the side-effecting operator {other:?}"),
2062 "C2",
2063 ));
2064 }
2065 };
2066 Ok(Expr::Unary(uop, Box::new(a), rty))
2067 }
2068 ExpressionKind::BinaryOp => self.binary(e, ty),
2069 ExpressionKind::ConditionalOp => {
2070 let mut bound = Vec::new();
2071 let r = self.conditional(e, ty, &mut bound);
2072 for uid in bound {
2073 self.pattern_binds.remove(&uid);
2074 }
2075 r
2076 }
2077 ExpressionKind::Conversion => self.conversion(e, ty),
2078 ExpressionKind::Concatenation => {
2079 let mut parts = Vec::new();
2080 let mut w = 0u32;
2081 for o in e.operands() {
2082 let p = self.expr(o)?;
2083 w += p.ty().width;
2084 parts.push(p);
2085 }
2086 Ok(Expr::Concat(parts, Ty::unsigned(w)))
2087 }
2088 ExpressionKind::Replication => {
2089 let n = e
2090 .replication_count()
2091 .and_then(|c| c.constant_value())
2092 .and_then(|c| c.as_i64())
2093 .ok_or_else(|| BuildError::Missing {
2094 what: "constant count of a replication".into(),
2095 })?;
2096 if n < 1 {
2097 return Err(unsupported("a replication with a count below 1", "C2"));
2098 }
2099 let inner =
2100 self.expr(e.replication_concat().ok_or_else(|| BuildError::Missing {
2101 what: "operand of a replication".into(),
2102 })?)?;
2103 let w = inner.ty().width * n as u32;
2104 Ok(Expr::Concat(vec![inner; n as usize], Ty::unsigned(w)))
2105 }
2106 ExpressionKind::Inside => self.inside(e),
2107 ExpressionKind::ElementSelect => self.element_select(e, ty),
2108 ExpressionKind::RangeSelect => self.range_select(e, ty),
2109 ExpressionKind::Call => {
2110 if e.is_system_call() == Some(true)
2111 && self.frame.is_none()
2112 && let Some(r) = self.array_call(e, ty)
2113 {
2114 return r;
2115 }
2116 let name = e.call_subroutine_name().unwrap_or_default();
2117 if e.is_system_call() == Some(true) && (name == "$signed" || name == "$unsigned") {
2118 let args = e.operands();
2119 let [arg] = args.as_slice() else {
2120 return Err(BuildError::Missing {
2121 what: format!("single argument of {name}"),
2122 });
2123 };
2124 let a = self.expr(*arg)?;
2125 return Ok(Expr::Resize(Box::new(a), ty));
2126 }
2127 if e.is_system_call() == Some(true)
2128 && let Some(r) = self.bit_count_call(e, ty)
2129 {
2130 return r;
2131 }
2132 if e.is_system_call() == Some(true) {
2133 return Err(unsupported(format!("a call to `{name}`"), "C13"));
2134 }
2135 self.user_call(e, ty)
2136 }
2137 ExpressionKind::LValueReference => self.lvalue_reference(),
2138 ExpressionKind::Dist => Err(BuildError::BadDist {
2139 what: "a `dist` nested inside another expression (it is a constraint item, \
2140 never an operand)"
2141 .into(),
2142 }),
2143 ExpressionKind::UnboundedLiteral => Err(unsupported(
2144 "an unbounded `$` outside an `inside` range",
2145 "C2",
2146 )),
2147 other => Err(unsupported(format!("a `{other:?}` expression"), "C2")),
2148 }
2149 }
2150
2151 fn conditional(
2157 &mut self,
2158 e: Expression<'_>,
2159 ty: Ty,
2160 bound: &mut Vec<SymbolId>,
2161 ) -> Result<Expr, BuildError> {
2162 let mut tests = Vec::new();
2163 for c in e.conditions() {
2164 let subject = self.expr(c.expr())?;
2165 tests.push(match c.pattern() {
2166 None => to_bool(subject),
2167 Some(p) => self.pattern_test(&p, subject, bound)?,
2168 });
2169 }
2170 let t = self.expr(e.true_value().ok_or_else(|| BuildError::Missing {
2171 what: "true arm of a conditional".into(),
2172 })?)?;
2173 for uid in bound.drain(..) {
2175 self.pattern_binds.remove(&uid);
2176 }
2177 let f = self.expr(e.false_value().ok_or_else(|| BuildError::Missing {
2178 what: "false arm of a conditional".into(),
2179 })?)?;
2180 Ok(Expr::Ite(
2181 Box::new(and_all(tests)),
2182 Box::new(t),
2183 Box::new(f),
2184 ty,
2185 ))
2186 }
2187
2188 fn pattern_test(
2195 &mut self,
2196 pat: &sv_lang::Pattern<'_>,
2197 subject: Expr,
2198 bound: &mut Vec<SymbolId>,
2199 ) -> Result<Expr, BuildError> {
2200 match pat.kind() {
2201 PatternKind::Constant => {
2202 let v = self.expr(pat.value_expr().ok_or_else(|| BuildError::Missing {
2203 what: "value of a constant pattern".into(),
2204 })?)?;
2205 let (a, b) = (subject.ty(), v.ty());
2206 let ct = Ty {
2207 width: a.width.max(b.width),
2208 signed: a.signed && b.signed,
2209 };
2210 Ok(bin(BinOp::Eq, coerce(subject, ct), coerce(v, ct), Ty::BIT))
2211 }
2212 PatternKind::Wildcard => Ok(Expr::Const(Ty::BIT, BigUint::one())),
2213 PatternKind::Variable => {
2214 if let Some(var) = pat.variable()
2215 && !var.name().is_empty()
2216 {
2217 let uid = var.uid();
2218 self.pattern_binds.insert(uid.clone(), subject);
2219 bound.push(uid);
2220 }
2221 Ok(Expr::Const(Ty::BIT, BigUint::one()))
2222 }
2223 other => Err(unsupported(
2224 format!(
2225 "a {other:?} pattern in a constraint (it matches a tagged union or an \
2226 unpacked struct, which is never a random variable)"
2227 ),
2228 "C2",
2229 )),
2230 }
2231 }
2232
2233 fn binary(&mut self, e: Expression<'_>, ty: Ty) -> Result<Expr, BuildError> {
2234 let op = e.binary_op().ok_or_else(|| BuildError::Missing {
2235 what: "operator of a binary expression".into(),
2236 })?;
2237 let (lhs, rhs) = match (e.left(), e.right()) {
2238 (Some(l), Some(r)) => (l, r),
2239 _ => {
2240 return Err(BuildError::Missing {
2241 what: "operands of a binary expression".into(),
2242 });
2243 }
2244 };
2245 let (bop, rty) = match op {
2246 BinaryOp::CaseEquality => return Err(BuildError::FourStateOperator { op: "===" }),
2247 BinaryOp::CaseInequality => return Err(BuildError::FourStateOperator { op: "!==" }),
2248 BinaryOp::WildcardEquality => return Err(BuildError::FourStateOperator { op: "==?" }),
2249 BinaryOp::WildcardInequality => {
2250 return Err(BuildError::FourStateOperator { op: "!=?" });
2251 }
2252 BinaryOp::Power => {
2253 let l = self.expr(lhs)?;
2254 let r = self.expr(rhs)?;
2255 return Ok(power(l, r, ty));
2256 }
2257 BinaryOp::Add => (BinOp::Add, ty),
2258 BinaryOp::Subtract => (BinOp::Sub, ty),
2259 BinaryOp::Multiply => (BinOp::Mul, ty),
2260 BinaryOp::Divide => (BinOp::Div, ty),
2261 BinaryOp::Mod => (BinOp::Mod, ty),
2262 BinaryOp::BinaryAnd => (BinOp::And, ty),
2263 BinaryOp::BinaryOr => (BinOp::Or, ty),
2264 BinaryOp::BinaryXor => (BinOp::Xor, ty),
2265 BinaryOp::BinaryXnor => (BinOp::Xnor, ty),
2266 BinaryOp::Equality => (BinOp::Eq, Ty::BIT),
2267 BinaryOp::Inequality => (BinOp::Ne, Ty::BIT),
2268 BinaryOp::GreaterThanEqual => (BinOp::Ge, Ty::BIT),
2269 BinaryOp::GreaterThan => (BinOp::Gt, Ty::BIT),
2270 BinaryOp::LessThanEqual => (BinOp::Le, Ty::BIT),
2271 BinaryOp::LessThan => (BinOp::Lt, Ty::BIT),
2272 BinaryOp::LogicalAnd => (BinOp::LogAnd, Ty::BIT),
2273 BinaryOp::LogicalOr => (BinOp::LogOr, Ty::BIT),
2274 BinaryOp::LogicalImplication => (BinOp::LogImpl, Ty::BIT),
2275 BinaryOp::LogicalEquivalence => (BinOp::LogEquiv, Ty::BIT),
2276 BinaryOp::LogicalShiftLeft | BinaryOp::ArithmeticShiftLeft => (BinOp::Shl, ty),
2277 BinaryOp::LogicalShiftRight => (BinOp::Shr, ty),
2278 BinaryOp::ArithmeticShiftRight => (BinOp::Ashr, ty),
2279 };
2280 let whole = |t: Option<Type<'_>>| {
2286 t.is_some_and(|t| {
2287 let c = t.canonical();
2288 c.is_unpacked_array() || c.is_unpacked_struct()
2289 })
2290 };
2291 if matches!(bop, BinOp::Eq | BinOp::Ne) && whole(lhs.expr_type()) && whole(rhs.expr_type())
2292 {
2293 let chains = aggregate::aggregate_chain(lhs, &self.forced_roots)
2294 .zip(aggregate::aggregate_chain(rhs, &self.forced_roots));
2295 let Some((lc, rc)) = chains else {
2296 return Err(unsupported(
2297 "an array comparison whose operand is not a property (or a member of a \
2298 struct property) of this class",
2299 "C14",
2300 ));
2301 };
2302 let l = self.aggregate_bitstream(lc)?;
2303 let r = self.aggregate_bitstream(rc)?;
2304 return Ok(bin(bop, l, r, rty));
2305 }
2306 let is_string =
2310 |e: Expression<'_>| e.expr_type().is_some_and(|t| t.canonical().is_string());
2311 if is_string(lhs) || is_string(rhs) {
2312 if !matches!(bop, BinOp::Eq | BinOp::Ne) {
2313 return Err(unsupported(
2314 format!("the string operator {op:?} (only `==` and `!=` compare strings)"),
2315 "C13",
2316 ));
2317 }
2318 let l = self.string_id(lhs)?;
2319 let r = self.string_id(rhs)?;
2320 return Ok(bin(bop, l, r, rty));
2321 }
2322 let l = self.expr(lhs)?;
2323 let r = self.expr(rhs)?;
2324 Ok(bin(bop, l, r, rty))
2325 }
2326
2327 fn string_id(&mut self, e: Expression<'_>) -> Result<Expr, BuildError> {
2331 if let Some(cv) = e.constant_value()
2332 && let Some(s) = cv.as_str()
2333 {
2334 let id = crate::strings::intern(s.as_bytes());
2335 return Ok(Expr::Const(HANDLE_TY, BigUint::from(id)));
2336 }
2337 match e.kind() {
2338 ExpressionKind::Conversion => {
2339 let inner = e.conversion_operand().ok_or_else(|| BuildError::Missing {
2340 what: "operand of a conversion".into(),
2341 })?;
2342 self.string_id(inner)
2343 }
2344 ExpressionKind::NamedValue
2345 | ExpressionKind::HierarchicalValue
2346 | ExpressionKind::MemberAccess => {
2347 let sym = e.referenced_symbol().or_else(|| e.member_symbol());
2348 if sym.is_some_and(|s| self.var_ix.contains_key(&s.uid())) {
2349 return Err(unsupported("a `rand` string", "C13"));
2350 }
2351 self.handle_state(e, HANDLE_TY)
2352 }
2353 other => Err(unsupported(
2354 format!("a `{other:?}` string operand of a comparison"),
2355 "C13",
2356 )),
2357 }
2358 }
2359
2360 fn conversion(&mut self, e: Expression<'_>, to: Ty) -> Result<Expr, BuildError> {
2361 let operand = e.conversion_operand().ok_or_else(|| BuildError::Missing {
2362 what: "operand of a conversion".into(),
2363 })?;
2364 let kind = e.conversion_kind().ok_or_else(|| BuildError::Missing {
2365 what: "kind of a conversion".into(),
2366 })?;
2367 if operand.expr_type().is_some_and(|t| !t.is_integral())
2371 && let Some(chain) = aggregate::aggregate_chain(operand, &self.forced_roots)
2372 {
2373 let x = self.aggregate_bitstream(chain)?;
2374 return Ok(if x.ty() == to {
2375 x
2376 } else {
2377 Expr::Resize(Box::new(x), to)
2378 });
2379 }
2380 let x = self.expr(operand)?;
2381 match kind {
2382 1 => Ok(coerce(x, to)),
2384 0 | 3 => Ok(if x.ty() == to {
2386 x
2387 } else {
2388 Expr::Resize(Box::new(x), to)
2389 }),
2390 other => Err(unsupported(
2391 format!("a conversion of kind {other} (streaming or bit-stream cast)"),
2392 "C2",
2393 )),
2394 }
2395 }
2396
2397 fn range_op(&self, item: Expression<'_>) -> Result<RangeOp, BuildError> {
2400 for tree in self.trees {
2401 if let Some(node) = item.syntax(tree) {
2402 let r =
2403 ValueRangeExpressionSyntax::cast(node).ok_or_else(|| BuildError::Missing {
2404 what: "value-range syntax for a ValueRange expression".into(),
2405 })?;
2406 let op = r.op().ok_or_else(|| BuildError::Missing {
2407 what: "operator of a value range".into(),
2408 })?;
2409 return match op.text() {
2410 ":" => Ok(RangeOp::Span),
2411 "+/-" => Ok(RangeOp::Abs),
2412 "+%-" => Ok(RangeOp::Pct),
2413 other => Err(unsupported(
2414 format!("the value-range operator `{other}`"),
2415 "C7",
2416 )),
2417 };
2418 }
2419 }
2420 Err(BuildError::Missing {
2421 what: "syntax node for a value range (is its syntax tree passed to the builder?)"
2422 .into(),
2423 })
2424 }
2425
2426 fn value_range(&mut self, item: Expression<'_>, subject: Ty) -> Result<Bounds, BuildError> {
2430 let (l, r) = match (item.left(), item.right()) {
2431 (Some(l), Some(r)) => (l, r),
2432 _ => {
2433 return Err(BuildError::Missing {
2434 what: "bounds of a value range".into(),
2435 });
2436 }
2437 };
2438 let op = self.range_op(item)?;
2439 let is_unbounded = |b: Expression<'_>| {
2441 let mut peeled = b;
2442 while peeled.kind() == ExpressionKind::Conversion {
2443 match peeled.conversion_operand() {
2444 Some(inner) => peeled = inner,
2445 None => break,
2446 }
2447 }
2448 peeled.kind() == ExpressionKind::UnboundedLiteral
2449 };
2450 if op == RangeOp::Span {
2451 let mut side = |b: Expression<'_>| -> Result<Option<(Ty, Expr)>, BuildError> {
2452 if is_unbounded(b) {
2453 return Ok(None);
2454 }
2455 let bx = self.expr(b)?;
2456 let t = common_ty(subject, bx.ty());
2457 Ok(Some((t, coerce(bx, t))))
2458 };
2459 let lo = side(l)?;
2460 let hi = side(r)?;
2461 return Ok(Bounds { lo, hi });
2462 }
2463 if is_unbounded(l) || is_unbounded(r) {
2464 return Err(BuildError::BadDist {
2465 what: "`$` as an operand of a `+/-` or `+%-` range".into(),
2466 });
2467 }
2468 let a = self.expr(l)?;
2469 let b = self.expr(r)?;
2470 let wide = Ty::signed(2 * subject.width.max(a.ty().width).max(b.ty().width) + 8);
2471 let (a, b) = (coerce(a, wide), coerce(b, wide));
2472 let delta = match op {
2473 RangeOp::Abs => b,
2474 _ => {
2475 let hundred = iconst(wide, 100);
2476 bin(
2477 BinOp::Div,
2478 bin(BinOp::Mul, a.clone(), b, wide),
2479 hundred,
2480 wide,
2481 )
2482 }
2483 };
2484 let lo = bin(BinOp::Sub, a.clone(), delta.clone(), wide);
2485 let hi = bin(BinOp::Add, a, delta, wide);
2486 Ok(Bounds {
2487 lo: Some((wide, lo)),
2488 hi: Some((wide, hi)),
2489 })
2490 }
2491
2492 fn dist(&mut self, e: Expression<'_>) -> Result<Expr, BuildError> {
2496 let runtime = self.in_runtime_loop();
2500 let saved = if runtime {
2501 self.no_arrays
2502 } else {
2503 self.no_arrays.replace("a `dist` constraint")
2504 };
2505 let r = self.dist_item(e, runtime);
2506 self.no_arrays = saved;
2507 r
2508 }
2509
2510 fn dist_item(&mut self, e: Expression<'_>, runtime: bool) -> Result<Expr, BuildError> {
2511 let bad = |what: &str| BuildError::BadDist { what: what.into() };
2512 let left = e.dist_left().ok_or_else(|| BuildError::Missing {
2513 what: "distributed expression of a `dist`".into(),
2514 })?;
2515 let mut left = left;
2520 while left.kind() == ExpressionKind::Conversion {
2521 match left.conversion_operand() {
2522 Some(inner) => left = inner,
2523 None => break,
2524 }
2525 }
2526 let subject = self.expr(left)?;
2527 let mut vars: Vec<VarId> = Vec::new();
2528 let mut randc: Option<VarId> = None;
2529 subject.for_each_var(&mut |v, _| {
2530 vars.push(v);
2531 if self.vars[v as usize].randc {
2532 randc = Some(v);
2533 }
2534 });
2535 vars.sort_unstable();
2536 vars.dedup();
2537 if let Some(v) = randc {
2538 return Err(BuildError::BadDist {
2539 what: format!(
2540 "`randc` variable `{}` in a `dist` (randc variables take no weights)",
2541 self.vars[v as usize].name
2542 ),
2543 });
2544 }
2545 let plain_var = match &subject {
2546 Expr::Var(v, _) => Some(*v),
2547 _ => None,
2548 };
2549 let sty = subject.ty();
2550 let state_only = |x: &Expr, what: &str| -> Result<(), BuildError> {
2551 let mut has_var = false;
2552 x.for_each_var(&mut |_, _| has_var = true);
2553 if has_var {
2554 return Err(BuildError::BadDist {
2555 what: format!(
2556 "a random variable in {what}; weights and bounds are fixed values"
2557 ),
2558 });
2559 }
2560 Ok(())
2561 };
2562 let weight_of = |this: &mut Self,
2563 w: Option<sv_lang::DistWeight<'_>>|
2564 -> Result<(Expr, bool), BuildError> {
2565 match w {
2566 None => Ok((Expr::constant(Ty::unsigned(32), 1u32), false)),
2568 Some(w) => {
2569 let x = this.expr(w.expr)?;
2570 state_only(&x, "a `dist` weight")?;
2571 Ok((x, w.kind == DistWeightKind::PerRange))
2572 }
2573 }
2574 };
2575 let mut items = Vec::new();
2576 for it in e.dist_items() {
2577 let v = it.value();
2578 let (member, ty, span) = if v.kind() == ExpressionKind::ValueRange {
2579 let b = self.value_range(v, sty)?;
2580 let ty = match (&b.lo, &b.hi) {
2583 (Some((t, _)), _) | (_, Some((t, _))) => *t,
2584 (None, None) => sty,
2585 };
2586 let mut conj = Vec::new();
2587 for (bound, cmp) in [(&b.lo, BinOp::Ge), (&b.hi, BinOp::Le)] {
2588 if let Some((t, x)) = bound {
2589 state_only(x, "a `dist` range bound")?;
2590 conj.push(bin(cmp, coerce(subject.clone(), *t), x.clone(), Ty::BIT));
2591 }
2592 }
2593 (
2594 and_all(conj),
2595 ty,
2596 Span::Range {
2597 lo: b.lo.map(|(_, x)| x),
2598 hi: b.hi.map(|(_, x)| x),
2599 },
2600 )
2601 } else {
2602 let x = self.expr(v)?;
2603 state_only(&x, "a `dist` value")?;
2604 let t = common_ty(sty, x.ty());
2605 let x = coerce(x, t);
2606 let m = bin(BinOp::Eq, coerce(subject.clone(), t), x.clone(), Ty::BIT);
2607 (m, t, Span::Point(x))
2608 };
2609 let (w, per_range) = weight_of(self, it.weight())?;
2610 items.push(Item {
2611 member,
2612 ty,
2613 span,
2614 weight: w,
2615 per_range,
2616 });
2617 }
2618 let default = match e.dist_default_weight() {
2619 Some(w) => {
2620 let (weight, per_range) = weight_of(self, Some(w))?;
2621 Some(DefaultWeight { weight, per_range })
2622 }
2623 None => None,
2624 };
2625 if items.is_empty() && default.is_none() {
2626 return Err(bad("a `dist` with no items"));
2627 }
2628 let d = Dist {
2629 subject_ty: sty,
2630 plain_var,
2631 vars,
2632 items,
2633 default,
2634 subject: runtime.then_some(subject),
2635 };
2636 let sel = SELECTOR_BASE + self.dists.len() as u32;
2637 let conjunct = d.conjunct(sel);
2638 self.dists.push(d);
2639 Ok(conjunct)
2640 }
2641
2642 fn inside(&mut self, e: Expression<'_>) -> Result<Expr, BuildError> {
2643 let left = self.expr(e.inside_left().ok_or_else(|| BuildError::Missing {
2644 what: "left operand of `inside`".into(),
2645 })?)?;
2646 let items = e.inside_range_list();
2647 if items.is_empty() {
2648 return Err(BuildError::Missing {
2649 what: "candidates of `inside`".into(),
2650 });
2651 }
2652 let mut acc: Option<Expr> = None;
2657 for item in items {
2658 let term = if item.kind() == ExpressionKind::ValueRange {
2659 let b = self.value_range(item, left.ty())?;
2662 let mut conj: Vec<Expr> = Vec::new();
2663 for (bound, cmp) in [(b.lo, BinOp::Ge), (b.hi, BinOp::Le)] {
2664 if let Some((t, x)) = bound {
2665 conj.push(bin(cmp, coerce(left.clone(), t), x, Ty::BIT));
2666 }
2667 }
2668 and_all(conj)
2669 } else if item
2670 .expr_type()
2671 .is_some_and(|t| arrays::array_shape(t).is_some())
2672 {
2673 self.inside_array(&left, item)?
2674 } else {
2675 let ix = self.expr(item)?;
2676 let t = common_ty(left.ty(), ix.ty());
2677 bin(BinOp::Eq, coerce(left.clone(), t), coerce(ix, t), Ty::BIT)
2678 };
2679 acc = Some(match acc {
2680 None => term,
2681 Some(prev) => bin(BinOp::LogOr, prev, term, Ty::BIT),
2682 });
2683 }
2684 Ok(acc.expect("candidates non-empty, checked above"))
2685 }
2686
2687 fn select_base(&mut self, e: Expression<'_>) -> Result<(Expr, Geom), BuildError> {
2689 let base = e.select_value().ok_or_else(|| BuildError::Missing {
2690 what: "base of a select".into(),
2691 })?;
2692 let bt = base.expr_type().ok_or_else(|| BuildError::Missing {
2693 what: "type of a select base".into(),
2694 })?;
2695 let bx = self.expr(base)?;
2696 let geom = Geom::of(bt, bx.ty().width, "a select base")?;
2697 Ok((bx, geom))
2698 }
2699
2700 fn wide_index(&mut self, idx: Expression<'_>) -> Result<(Expr, Ty), BuildError> {
2703 let ix = self.expr(idx)?;
2704 let wide = Ty::signed(ix.ty().width.max(32) + 2);
2705 Ok((Expr::Resize(Box::new(ix), wide), wide))
2706 }
2707
2708 fn dyn_window(base: Expr, pos: Expr, wide: Ty, ew: u32, out_width: u32, ty: Ty) -> Expr {
2710 let off = if ew > 1 {
2711 bin(BinOp::Mul, pos, iconst(wide, i64::from(ew)), wide)
2712 } else {
2713 pos
2714 };
2715 let bty = base.ty();
2716 let shifted = bin(BinOp::Shr, base, off, bty);
2717 Expr::Resize(
2718 Box::new(Expr::Resize(Box::new(shifted), Ty::unsigned(out_width))),
2719 ty,
2720 )
2721 }
2722
2723 fn static_window(
2724 base: Expr,
2725 geom: &Geom,
2726 lo_idx: i64,
2727 hi_idx: i64,
2728 what: &str,
2729 ty: Ty,
2730 ) -> Result<Expr, BuildError> {
2731 let (pl, ph) = (geom.pos(lo_idx), geom.pos(hi_idx));
2732 let (low, high) = (pl.min(ph), pl.max(ph));
2733 if low < 0 || high >= geom.elems() {
2734 return Err(BuildError::OutOfRangeSelect {
2735 what: format!(
2736 "{what}[{lo_idx}..{hi_idx}] outside [{}:{}]",
2737 geom.left, geom.right
2738 ),
2739 });
2740 }
2741 let width = (high - low + 1) as u32 * geom.ew;
2742 let slice = Expr::Slice(Box::new(base), low as u32 * geom.ew, Ty::unsigned(width));
2743 Ok(if slice.ty() == ty {
2744 slice
2745 } else {
2746 Expr::Resize(Box::new(slice), ty)
2747 })
2748 }
2749
2750 fn element_select(&mut self, e: Expression<'_>, ty: Ty) -> Result<Expr, BuildError> {
2751 if let Some(chain) = aggregate::aggregate_chain(e, &self.forced_roots)
2753 && !chain.steps.is_empty()
2754 {
2755 return self.aggregate_value(chain, ty);
2756 }
2757 if let Some(arr) = e.select_value()
2758 && arr
2759 .expr_type()
2760 .is_some_and(|t| arrays::array_shape(t).is_some())
2761 {
2762 self.not_in_function("an array element")?;
2763 return self.array_element(e, arr, ty);
2764 }
2765 let (base, geom) = self.select_base(e)?;
2766 let sel = e.selector().ok_or_else(|| BuildError::Missing {
2767 what: "index of an element select".into(),
2768 })?;
2769 if let Some(i) = sel.constant_value().and_then(|c| c.as_i64()) {
2770 return Self::static_window(base, &geom, i, i, "element select", ty);
2771 }
2772 let (idx, wide) = self.wide_index(sel)?;
2773 let pos = if geom.descending() {
2774 bin(BinOp::Sub, idx, iconst(wide, geom.right), wide)
2775 } else {
2776 bin(BinOp::Sub, iconst(wide, geom.right), idx, wide)
2777 };
2778 Ok(Self::dyn_window(base, pos, wide, geom.ew, ty.width, ty))
2779 }
2780
2781 fn range_select(&mut self, e: Expression<'_>, ty: Ty) -> Result<Expr, BuildError> {
2782 let (base, geom) = self.select_base(e)?;
2783 let kind = e
2784 .range_selection_kind()
2785 .ok_or_else(|| BuildError::Missing {
2786 what: "kind of a range select".into(),
2787 })?;
2788 let (l, r) = match (e.range_left(), e.range_right()) {
2789 (Some(l), Some(r)) => (l, r),
2790 _ => {
2791 return Err(BuildError::Missing {
2792 what: "bounds of a range select".into(),
2793 });
2794 }
2795 };
2796 let const_of = |x: Expression<'_>| x.constant_value().and_then(|c| c.as_i64());
2797 if kind == 0 {
2798 let (Some(a), Some(b)) = (const_of(l), const_of(r)) else {
2799 return Err(BuildError::Missing {
2800 what: "constant bounds of a `[l:r]` select".into(),
2801 });
2802 };
2803 return Self::static_window(base, &geom, a.min(b), a.max(b), "range select", ty);
2804 }
2805 let w = const_of(r)
2806 .filter(|w| *w >= 1)
2807 .ok_or_else(|| BuildError::Missing {
2808 what: "constant width of an indexed part select".into(),
2809 })?;
2810 let (dlo, dhi) = if kind == 1 { (0, w - 1) } else { (-(w - 1), 0) };
2812 if let Some(b) = const_of(l) {
2813 return Self::static_window(base, &geom, b + dlo, b + dhi, "part select", ty);
2814 }
2815 let (idx, wide) = self.wide_index(l)?;
2816 let pos = if geom.descending() {
2817 bin(BinOp::Add, idx, iconst(wide, dlo - geom.right), wide)
2818 } else {
2819 bin(BinOp::Sub, iconst(wide, geom.right - dhi), idx, wide)
2820 };
2821 Ok(Self::dyn_window(base, pos, wide, geom.ew, ty.width, ty))
2822 }
2823}