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 templates: HashMap<u32, u32>,
562 loops: Vec<LoopScope>,
564 next_loop: u32,
565 no_arrays: Option<&'static str>,
568 funcs: Vec<Func>,
570 func_ix: HashMap<(SymbolId, Vec<SymbolId>), u32>,
571 fn_base: Option<funcs::FnBase>,
575 frame: Option<funcs::Frame>,
577 softs: Vec<Soft>,
579 disables: Vec<Disable>,
580 scope: (u32, u32),
583 seq: u32,
584 guard_depth: u32,
586 local_of: Option<LocalClassifier<'t>>,
589 captures: Vec<CaptureInfo>,
590 capture_ix: HashMap<Vec<SymbolId>, usize>,
591 blocks: Vec<BlockInfo>,
593}
594
595type VirtualTargets<'t> =
600 Box<dyn for<'s> Fn(Symbol<'s>, Symbol<'s>) -> Vec<(u32, Symbol<'t>)> + 't>;
601type VirtualResolver<'t> = Box<dyn for<'s> Fn(Symbol<'s>) -> Option<Symbol<'t>> + 't>;
602type SlotResolver<'t> = Box<dyn for<'s> Fn(Symbol<'s>) -> Result<FieldSlot, String> + 't>;
603
604#[derive(Clone, Copy, Debug)]
606enum SolveOperand {
607 Int(VarId),
608 Real(u32),
609 Size,
611}
612
613const HANDLE_TY: Ty = Ty::unsigned(64);
616
617#[derive(Clone, Copy, Debug, PartialEq, Eq)]
619enum RangeOp {
620 Span,
622 Abs,
624 Pct,
626}
627
628struct Bounds {
631 lo: Option<(Ty, Expr)>,
632 hi: Option<(Ty, Expr)>,
633}
634
635fn common_ty(a: Ty, b: Ty) -> Ty {
637 Ty {
638 width: a.width.max(b.width),
639 signed: a.signed && b.signed,
640 }
641}
642
643fn is_handle_type(t: Option<Type<'_>>) -> bool {
645 t.is_some_and(|t| {
646 let c = t.canonical();
647 c.is_class() || c.is_null()
648 })
649}
650
651fn ty_of(t: Option<Type<'_>>, what: &str) -> Result<Ty, BuildError> {
652 let t = t.ok_or_else(|| BuildError::Missing {
653 what: format!("type for {what}"),
654 })?;
655 if t.is_virtual_interface() {
656 return Err(BuildError::NonIntegral {
657 what: format!("{what} of virtual interface type"),
658 });
659 }
660 if !t.is_integral() {
661 return Err(BuildError::NonIntegral {
662 what: format!("{what} of type `{}`", t.to_sv_string()),
663 });
664 }
665 let width = u32::try_from(t.bit_width()).map_err(|_| BuildError::Missing {
666 what: format!("representable width for {what}"),
667 })?;
668 if width == 0 {
669 return Err(BuildError::NonIntegral {
670 what: format!("zero-width {what}"),
671 });
672 }
673 Ok(Ty {
674 width,
675 signed: t.is_signed(),
676 })
677}
678
679fn iconst(ty: Ty, v: i64) -> Expr {
682 let m = BigInt::one() << ty.width;
683 let r = ((BigInt::from(v) % &m) + &m) % &m;
684 Expr::Const(ty, r.to_biguint().expect("reduced into [0, 2^w)"))
685}
686
687fn svint_to_biguint(v: &SVInt, ty: Ty) -> Result<BigUint, BuildError> {
688 if v.has_unknown() {
689 return Err(BuildError::FourStateLiteral {
690 text: format!("{v:?}"),
691 });
692 }
693 let w = v.bit_width();
694 let bits = v.bits();
695 let mut acc = BigUint::zero();
696 for (i, b) in bits.iter().enumerate() {
697 if *b == Bit::One {
698 acc.set_bit(i as u64, true);
699 }
700 }
701 if ty.width > w && v.is_signed() && bits.last() == Some(&Bit::One) {
702 acc |= mask(ty.width) ^ mask(w);
703 }
704 Ok(acc & mask(ty.width))
705}
706
707fn coerce(x: Expr, to: Ty) -> Expr {
710 let from = x.ty();
711 if from == to {
712 return x;
713 }
714 if !to.signed && from.signed && to.width > from.width {
715 let unsigned = Expr::Resize(Box::new(x), Ty::unsigned(from.width));
716 return Expr::Resize(Box::new(unsigned), to);
717 }
718 Expr::Resize(Box::new(x), to)
719}
720
721fn to_bool(x: Expr) -> Expr {
722 if x.ty() == Ty::BIT {
723 return x;
724 }
725 let zero = Expr::Const(x.ty(), BigUint::zero());
726 Expr::Binary(BinOp::Ne, Box::new(x), Box::new(zero), Ty::BIT)
727}
728
729fn and_all(mut parts: Vec<Expr>) -> Expr {
730 if parts.is_empty() {
731 return Expr::Const(Ty::BIT, BigUint::one());
732 }
733 let first = parts.remove(0);
734 parts.into_iter().fold(to_bool(first), |acc, p| {
735 Expr::Binary(BinOp::LogAnd, Box::new(acc), Box::new(to_bool(p)), Ty::BIT)
736 })
737}
738
739fn power(base: Expr, exp: Expr, ty: Ty) -> Expr {
746 let one = || Expr::Const(ty, BigUint::from(1u8));
747 let ew = exp.ty().width;
748 let mag_bits = if exp.ty().signed { ew - 1 } else { ew };
749 let bit = |i: u32| Expr::Slice(Box::new(exp.clone()), i, Ty::BIT);
750 let mut acc = one();
751 let mut sq = base.clone();
752 for i in 0..mag_bits {
753 acc = Expr::Ite(
754 Box::new(bit(i)),
755 Box::new(bin(BinOp::Mul, acc.clone(), sq.clone(), ty)),
756 Box::new(acc),
757 ty,
758 );
759 if i + 1 < mag_bits {
760 sq = bin(BinOp::Mul, sq.clone(), sq, ty);
761 }
762 }
763 if !exp.ty().signed {
764 return acc;
765 }
766 let is = |v: Expr| bin(BinOp::Eq, base.clone(), v, Ty::BIT);
767 let mut neg = Expr::Const(ty, BigUint::default());
768 if ty.signed {
769 let minus_one = Expr::Const(ty, (BigUint::from(1u8) << ty.width) - 1u8);
770 let alt = Expr::Ite(
771 Box::new(bit(0)),
772 Box::new(minus_one.clone()),
773 Box::new(one()),
774 ty,
775 );
776 neg = Expr::Ite(Box::new(is(minus_one)), Box::new(alt), Box::new(neg), ty);
777 }
778 neg = Expr::Ite(Box::new(is(one())), Box::new(one()), Box::new(neg), ty);
779 Expr::Ite(Box::new(bit(ew - 1)), Box::new(neg), Box::new(acc), ty)
780}
781
782fn bin(op: BinOp, l: Expr, r: Expr, ty: Ty) -> Expr {
783 Expr::Binary(op, Box::new(l), Box::new(r), ty)
784}
785
786struct Geom {
788 left: i64,
789 right: i64,
790 ew: u32,
791}
792
793impl Geom {
794 fn descending(&self) -> bool {
795 self.left >= self.right
796 }
797 fn elems(&self) -> i64 {
798 (self.left - self.right).abs() + 1
799 }
800 fn pos(&self, i: i64) -> i64 {
802 if self.descending() {
803 i - self.right
804 } else {
805 self.right - i
806 }
807 }
808 fn of(ty: Type<'_>, width: u32, what: &str) -> Result<Geom, BuildError> {
809 if ty.is_packed_array() {
810 let r = ty.packed_array_range();
811 let ew = ty
812 .element_type()
813 .map_or(1, |e| u32::try_from(e.bit_width()).unwrap_or(1).max(1));
814 if u64::from(r.width()) * u64::from(ew) == u64::from(width) {
815 return Ok(Geom {
816 left: i64::from(r.left),
817 right: i64::from(r.right),
818 ew,
819 });
820 }
821 } else if ty.is_integral() && !ty.is_struct() {
822 let r = ty.bit_vector_range();
823 if r.width() == width {
824 return Ok(Geom {
825 left: i64::from(r.left),
826 right: i64::from(r.right),
827 ew: 1,
828 });
829 }
830 }
831 Err(unsupported(
832 format!("a select into {what} of type `{}`", ty.to_sv_string()),
833 "C11",
834 ))
835 }
836}
837
838pub fn handle_path_uids(e: Expression<'_>) -> Option<Vec<SymbolId>> {
841 Builder::handle_path(e)
842 .ok()
843 .map(|p| p.iter().map(|s| s.uid()).collect())
844}
845
846impl<'t> Builder<'t> {
847 pub fn new(trees: &'t [SyntaxTree]) -> Self {
850 Builder {
851 trees,
852 vars: Vec::new(),
853 var_ix: HashMap::new(),
854 pattern_binds: HashMap::new(),
855 elem_ix: HashMap::new(),
856 forced_roots: HashSet::new(),
857 real_vars: Vec::new(),
858 real_var_ix: HashMap::new(),
859 real_leaf_ix: HashMap::new(),
860 leaf_state_ix: HashMap::new(),
861 real_conds: Vec::new(),
862 real_dists: Vec::new(),
863 real_before: Vec::new(),
864 states: Vec::new(),
865 state_ix: HashMap::new(),
866 slot_of: None,
867 virtual_impl: None,
868 virtual_targets: None,
869 conjuncts: Vec::new(),
870 before: Vec::new(),
871 dists: Vec::new(),
872 arrays: Vec::new(),
873 array_ix: HashMap::new(),
874 templates: HashMap::new(),
875 loops: Vec::new(),
876 next_loop: 0,
877 no_arrays: None,
878 funcs: Vec::new(),
879 func_ix: HashMap::new(),
880 fn_base: None,
881 frame: None,
882 softs: Vec::new(),
883 disables: Vec::new(),
884 scope: (0, 0),
885 seq: 0,
886 guard_depth: 0,
887 local_of: None,
888 captures: Vec::new(),
889 capture_ix: HashMap::new(),
890 blocks: Vec::new(),
891 }
892 }
893
894 pub fn set_local_classifier(&mut self, classify: impl for<'s> Fn(Expression<'s>) -> bool + 't) {
905 self.local_of = Some(Box::new(classify));
906 }
907
908 pub(super) fn is_call_local(&self, e: Expression<'_>) -> bool {
912 self.frame.is_none() && self.local_of.as_ref().is_some_and(|c| c(e))
913 }
914
915 fn capture_ref(&mut self, sym: Symbol<'_>, ty: Ty) -> Expr {
918 self.capture_path(sym, &[], ty)
919 }
920
921 fn capture_path(&mut self, sym: Symbol<'_>, hops: &[Symbol<'_>], ty: Ty) -> Expr {
924 let uid = sym.uid();
925 let key: Vec<SymbolId> = std::iter::once(uid.clone())
926 .chain(hops.iter().map(|h| h.uid()))
927 .collect();
928 if let Some(&ix) = self.capture_ix.get(&key) {
929 let state = self.captures[ix].state;
930 return Expr::State(state, self.states[state as usize].ty);
931 }
932 let name = hops
933 .iter()
934 .fold(sym.name().to_string(), |n, h| format!("{n}.{}", h.name()));
935 let state = self.states.len() as u32;
936 self.states.push(StateInfo {
937 root_bit: 0,
938 symbol: uid.clone(),
939 name: name.clone(),
940 ty,
941 via: Vec::new(),
942 window: None,
943 class_of: false,
944 });
945 self.capture_ix.insert(key, self.captures.len());
946 self.captures.push(CaptureInfo {
947 state,
948 symbol: uid,
949 hops: hops.iter().map(|h| h.uid()).collect(),
950 name,
951 ty,
952 });
953 Expr::State(state, ty)
954 }
955
956 pub fn set_priority_scope(&mut self, object: u32, level: u32) {
963 self.scope = (object, level);
964 }
965
966 fn next_priority(&mut self) -> Priority {
967 let p = Priority {
968 object: self.scope.0,
969 level: self.scope.1,
970 seq: self.seq,
971 };
972 self.seq += 1;
973 p
974 }
975
976 fn make_soft(&mut self, body: Expr, dists: std::ops::Range<usize>) -> Result<Expr, BuildError> {
979 if self.in_runtime_loop() || self.frame.is_some() {
980 return Err(BuildError::BadSoft {
981 what: "a `soft` constraint inside a `foreach` over a dynamic array or queue is \
982 not supported: it would be one soft constraint per run-time element"
983 .into(),
984 });
985 }
986 let dist_refs: Vec<&Dist> = self.dists[dists.clone()].iter().collect();
987 let keys = priority::keys_of(&body, &dist_refs);
988 for k in &keys {
989 if let SoftKey::Var(v) = k
990 && self.vars[*v as usize].randc
991 {
992 return Err(BuildError::BadSoft {
993 what: format!(
994 "a `soft` constraint on the `randc` variable `{}` (soft constraints \
995 apply to `rand` variables only)",
996 self.vars[*v as usize].name
997 ),
998 });
999 }
1000 }
1001 let id = self.softs.len() as u32;
1002 let priority = self.next_priority();
1003 self.softs.push(Soft {
1004 priority,
1005 dists: dists.map(|d| d as u32).collect(),
1006 keys,
1007 disabled: false,
1008 disabled_by: Vec::new(),
1009 });
1010 Ok(priority::wrap_soft(id, body))
1011 }
1012
1013 fn disable_soft(&mut self, c: Constraint<'_>) -> Result<(), BuildError> {
1015 if self.guard_depth > 0 || self.in_runtime_loop() {
1016 return Err(BuildError::BadSoft {
1017 what: "a `disable soft` inside an implication, an `if` or a `foreach` over a \
1018 dynamic array or queue is not supported (it discards unconditionally)"
1019 .into(),
1020 });
1021 }
1022 let target = c
1023 .children()
1024 .into_iter()
1025 .find_map(|n| n.as_expression())
1026 .ok_or_else(|| BuildError::Missing {
1027 what: "target expression of `disable soft`".into(),
1028 })?;
1029 let key = if let Some(arr) = Self::size_call_array(target) {
1030 SoftKey::ArraySize(self.array_of(arr)?)
1031 } else if let Some(chain) = aggregate::aggregate_chain(target, &self.forced_roots)
1032 && !chain.steps.is_empty()
1033 {
1034 let (leaf, declared) = self.declared_leaf(chain, "the target of `disable soft`")?;
1035 match declared {
1036 Some(Declared::Int(id)) => SoftKey::Var(id),
1037 Some(Declared::Real(_)) => {
1038 return Err(BuildError::BadSoft {
1039 what: "`disable soft` of a real variable is not supported (soft \
1040 constraints are relaxed over integral variables only)"
1041 .into(),
1042 });
1043 }
1044 None => {
1045 return Err(BuildError::BadSoft {
1046 what: format!(
1047 "`disable soft` names `{}`, which is not a `rand` variable of this \
1048 class",
1049 leaf.name
1050 ),
1051 });
1052 }
1053 }
1054 } else {
1055 let sym = match target.kind() {
1056 ExpressionKind::NamedValue => target.referenced_symbol(),
1057 ExpressionKind::MemberAccess => {
1058 let base = Self::operand(target, "a member access")?;
1059 let is_this = base.kind() == ExpressionKind::NamedValue
1060 && base.referenced_symbol().is_some_and(|s| s.name() == "this");
1061 if !is_this {
1062 return Err(BuildError::ForeignObjectAccess {
1063 what: "the target of `disable soft`".into(),
1064 });
1065 }
1066 target.member_symbol()
1067 }
1068 other => {
1069 return Err(BuildError::BadSoft {
1070 what: format!(
1071 "a `{other:?}` expression as the target of `disable soft` (name a \
1072 whole variable, `this.v` or `A.size`)"
1073 ),
1074 });
1075 }
1076 };
1077 let sym = sym.ok_or_else(|| BuildError::Missing {
1078 what: "symbol of the target of `disable soft`".into(),
1079 })?;
1080 let uid = sym.uid();
1081 if let Some(&id) = self.var_ix.get(&uid) {
1082 SoftKey::Var(id)
1083 } else if self.array_ix.contains_key(&uid) {
1084 SoftKey::Array(self.array_of(target)?)
1085 } else if self.real_var_ix.contains_key(&uid) {
1086 return Err(BuildError::BadSoft {
1087 what: "`disable soft` of a real variable is not supported (soft constraints \
1088 are relaxed over integral variables only)"
1089 .into(),
1090 });
1091 } else {
1092 return Err(BuildError::BadSoft {
1093 what: format!(
1094 "`disable soft` names `{}`, which is not a `rand` variable of this class",
1095 sym.name()
1096 ),
1097 });
1098 }
1099 };
1100 let priority = self.next_priority();
1101 self.disables.push(Disable { priority, key });
1102 Ok(())
1103 }
1104
1105 pub fn set_slot_resolver(
1110 &mut self,
1111 resolver: impl for<'s> Fn(Symbol<'s>) -> Result<FieldSlot, String> + 't,
1112 ) {
1113 self.slot_of = Some(Box::new(resolver));
1114 }
1115
1116 pub fn set_virtual_resolver(
1123 &mut self,
1124 resolver: impl for<'s> Fn(Symbol<'s>) -> Option<Symbol<'t>> + 't,
1125 ) {
1126 self.virtual_impl = Some(Box::new(resolver));
1127 }
1128
1129 pub fn set_virtual_targets(
1135 &mut self,
1136 resolver: impl for<'s> Fn(Symbol<'s>, Symbol<'s>) -> Vec<(u32, Symbol<'t>)> + 't,
1137 ) {
1138 self.virtual_targets = Some(Box::new(resolver));
1139 }
1140
1141 pub fn declare_rand(&mut self, sym: Symbol<'_>) -> Result<u32, BuildError> {
1144 let uid = sym.uid();
1145 if let Some(&id) = self.var_ix.get(&uid) {
1146 return Ok(id);
1147 }
1148 let name = sym.name().to_string();
1149 let randc = match sym.rand_mode() {
1150 RandMode::None => return Err(BuildError::NotRand { name }),
1151 RandMode::RandC => true,
1152 RandMode::Rand => false,
1153 };
1154 let vt = sym.value_type().ok_or_else(|| BuildError::Missing {
1155 what: format!("value type for `{name}`"),
1156 })?;
1157 let id = self.new_rand_var(uid.clone(), name, vt, randc, Vec::new(), None)?;
1158 self.var_ix.insert(uid, id);
1159 Ok(id)
1160 }
1161
1162 pub fn declare_var_forced(&mut self, sym: Symbol<'_>) -> Result<u32, BuildError> {
1169 let uid = sym.uid();
1170 if let Some(&id) = self.var_ix.get(&uid) {
1171 return Ok(id);
1172 }
1173 let name = sym.name().to_string();
1174 let randc = matches!(sym.rand_mode(), RandMode::RandC);
1175 let vt = sym.value_type().ok_or_else(|| BuildError::Missing {
1176 what: format!("value type for `{name}`"),
1177 })?;
1178 let id = self.new_rand_var(uid.clone(), name, vt, randc, Vec::new(), None)?;
1179 self.var_ix.insert(uid, id);
1180 Ok(id)
1181 }
1182
1183 fn enum_values(vt: Type<'_>, ty: Ty) -> Result<Option<Vec<BigUint>>, BuildError> {
1186 if !vt.is_enum() {
1187 return Ok(None);
1188 }
1189 let mut vals = Vec::new();
1190 for m in vt.canonical().enum_members() {
1191 let cv = m.constant_value().ok_or_else(|| BuildError::Missing {
1192 what: format!("value of enum member `{}`", m.name()),
1193 })?;
1194 let i = cv.as_integer().ok_or_else(|| BuildError::Missing {
1195 what: format!("integer value of enum member `{}`", m.name()),
1196 })?;
1197 vals.push(svint_to_biguint(i, ty)?);
1198 }
1199 Ok(Some(vals))
1200 }
1201
1202 fn new_rand_var(
1206 &mut self,
1207 uid: SymbolId,
1208 name: String,
1209 vt: Type<'_>,
1210 randc: bool,
1211 path: Vec<Step>,
1212 window: Option<Window>,
1213 ) -> Result<u32, BuildError> {
1214 if vt.is_virtual_interface() {
1215 return Err(BuildError::VirtualInterfaceRand { name });
1216 }
1217 let ty = ty_of(Some(vt), &format!("`rand` variable `{name}`"))?;
1218 if randc && ty.width > MAX_RANDC_WIDTH {
1219 return Err(BuildError::RandcTooWide {
1220 name,
1221 width: ty.width,
1222 max: MAX_RANDC_WIDTH,
1223 });
1224 }
1225 let enum_domain = Self::enum_values(vt, ty)?;
1226 let id = self.vars.len() as u32;
1227 self.vars.push(VarInfo {
1228 symbol: uid,
1229 name,
1230 ty,
1231 enum_domain,
1232 randc,
1233 path,
1234 window,
1235 });
1236 Ok(id)
1237 }
1238
1239 pub fn add_block(&mut self, block: Symbol<'_>) -> Result<(), BuildError> {
1241 let start = self.conjuncts.len();
1242 let from = [
1243 self.dists.len(),
1244 self.softs.len(),
1245 self.before.len(),
1246 self.real_conds.len(),
1247 self.real_dists.len(),
1248 self.real_before.len(),
1249 self.disables.len(),
1250 ];
1251 self.add_block_body(block)?;
1252 self.blocks.push(BlockInfo {
1253 conjuncts: start..self.conjuncts.len(),
1254 items: BlockItems {
1255 dists: from[0]..self.dists.len(),
1256 softs: from[1]..self.softs.len(),
1257 before: from[2]..self.before.len(),
1258 real_conds: from[3]..self.real_conds.len(),
1259 real_dists: from[4]..self.real_dists.len(),
1260 real_before: from[5]..self.real_before.len(),
1261 disables: from[6]..self.disables.len(),
1262 },
1263 });
1264 Ok(())
1265 }
1266
1267 fn add_block_body(&mut self, block: Symbol<'_>) -> Result<(), BuildError> {
1268 let tree = block
1269 .constraint_block_constraints()
1270 .ok_or_else(|| BuildError::Missing {
1271 what: format!("bound body for constraint block `{}`", block.name()),
1272 })?;
1273 let c = tree.as_constraint().ok_or_else(|| BuildError::Missing {
1274 what: format!("constraint body of block `{}`", block.name()),
1275 })?;
1276 let flags = block.constraint_block_flags();
1283 if c.kind() == ConstraintKind::Invalid
1284 && flags.contains(ConstraintBlockFlags::EXTERN)
1285 && !flags.contains(ConstraintBlockFlags::PURE)
1286 {
1287 if flags.contains(ConstraintBlockFlags::EXPLICIT_EXTERN) {
1288 return Err(BuildError::MissingExternBody {
1289 name: block.name().to_string(),
1290 });
1291 }
1292 return Ok(());
1293 }
1294 self.add_constraint(c)
1295 }
1296
1297 pub fn add_constraint(&mut self, c: Constraint<'_>) -> Result<(), BuildError> {
1299 let mut out = self.constraint(c)?;
1300 self.conjuncts.append(&mut out);
1301 Ok(())
1302 }
1303
1304 pub fn finish(mut self) -> Result<ConstraintSet, BuildError> {
1306 let mut domain_conjunct = vec![None; self.vars.len()];
1307 for (i, v) in self.vars.iter().enumerate() {
1308 let Some(domain) = &v.enum_domain else {
1309 continue;
1310 };
1311 let var = Expr::Var(i as u32, v.ty);
1312 let mut any: Option<Expr> = None;
1313 for val in domain {
1314 let eq = bin(
1315 BinOp::Eq,
1316 var.clone(),
1317 Expr::Const(v.ty, val.clone()),
1318 Ty::BIT,
1319 );
1320 any = Some(match any {
1321 None => eq,
1322 Some(prev) => bin(BinOp::LogOr, prev, eq, Ty::BIT),
1323 });
1324 }
1325 if let Some(e) = any {
1326 domain_conjunct[i] = Some(self.conjuncts.len());
1327 self.conjuncts.push(e);
1328 }
1329 }
1330 for c in &self.conjuncts {
1331 c.validate()?;
1332 }
1333 for d in &self.dists {
1334 d.validate()?;
1335 }
1336 order::levels(self.vars.len(), &self.before)?;
1337 let (real_vars, real) = self.finish_real()?;
1338 priority::apply_disables(&mut self.softs, &self.disables);
1339 Ok(ConstraintSet {
1340 vars: self.vars,
1341 states: self.states,
1342 conjuncts: self.conjuncts,
1343 domain_conjunct,
1344 before: self.before,
1345 dists: self.dists,
1346 real_vars,
1347 real,
1348 arrays: self.arrays,
1349 funcs: self.funcs,
1350 softs: self.softs,
1351 captures: self.captures,
1352 blocks: self.blocks,
1353 disables: self.disables.len(),
1354 disable_keys: self.disables.iter().map(|d| d.key).collect(),
1355 })
1356 }
1357
1358 fn solve_side_len(&self, c: Constraint<'_>) -> Result<usize, BuildError> {
1362 for tree in self.trees {
1363 if let Some(node) = c.syntax(tree) {
1364 let s =
1365 SolveBeforeConstraintSyntax::cast(node).ok_or_else(|| BuildError::Missing {
1366 what: "`solve ... before` syntax for a SolveBefore constraint".into(),
1367 })?;
1368 return Ok(s.before_expr().iter().count());
1369 }
1370 }
1371 Err(BuildError::Missing {
1372 what: "syntax node for a constraint (is its syntax tree passed to the builder?)".into(),
1373 })
1374 }
1375
1376 fn solve_operand(&mut self, e: Expression<'_>) -> Result<SolveOperand, BuildError> {
1380 if let Some(chain) = aggregate::aggregate_chain(e, &self.forced_roots)
1383 && !chain.steps.is_empty()
1384 {
1385 let (leaf, declared) = self.declared_leaf(chain, "a `solve ... before` operand")?;
1386 if leaf.randc() {
1387 return Err(BuildError::BadSolveOperand {
1388 what: format!(
1389 "`{}` is `randc`, which is always solved before every `rand` variable",
1390 leaf.name
1391 ),
1392 });
1393 }
1394 return match declared {
1395 Some(Declared::Int(id)) => Ok(SolveOperand::Int(id)),
1396 Some(Declared::Real(id)) => Ok(SolveOperand::Real(id)),
1397 None => Err(BuildError::BadSolveOperand {
1398 what: format!("`{}` is not a `rand` variable of this class", leaf.name),
1399 }),
1400 };
1401 }
1402 let sym = match e.kind() {
1403 ExpressionKind::NamedValue => e.referenced_symbol(),
1404 ExpressionKind::MemberAccess => {
1405 let base = Self::operand(e, "a member access")?;
1406 let is_this = base.kind() == ExpressionKind::NamedValue
1407 && base.referenced_symbol().is_some_and(|s| s.name() == "this");
1408 if !is_this {
1409 return Err(BuildError::ForeignObjectAccess {
1410 what: format!(
1411 "`solve ... before` operand `.{}`",
1412 e.member_symbol()
1413 .map_or(String::new(), |m| m.name().to_string())
1414 ),
1415 });
1416 }
1417 e.member_symbol()
1418 }
1419 ExpressionKind::Call => {
1420 let Some(arr) = Self::size_call_array(e) else {
1421 return Err(unsupported(
1422 "a call other than `array.size` as a `solve ... before` operand",
1423 "C11",
1424 ));
1425 };
1426 self.arrays_allowed("an array size")?;
1427 self.array_of(arr)?;
1428 return Ok(SolveOperand::Size);
1429 }
1430 other => {
1431 return Err(unsupported(
1432 format!("a `{other:?}` expression as a `solve ... before` operand"),
1433 "C11",
1434 ));
1435 }
1436 };
1437 let sym = sym.ok_or_else(|| BuildError::Missing {
1438 what: "symbol of a `solve ... before` operand".into(),
1439 })?;
1440 let name = sym.name().to_string();
1441 if sym.rand_mode() == RandMode::RandC {
1442 return Err(BuildError::BadSolveOperand {
1443 what: format!(
1444 "`{name}` is `randc`, which is always solved before every `rand` variable"
1445 ),
1446 });
1447 }
1448 let uid = sym.uid();
1449 if let Some(&id) = self.var_ix.get(&uid) {
1450 return Ok(SolveOperand::Int(id));
1451 }
1452 match self.real_var_ix.get(&uid) {
1453 Some(&id) => Ok(SolveOperand::Real(id)),
1454 None => Err(BuildError::BadSolveOperand {
1455 what: format!("`{name}` is not a `rand` variable of this class"),
1456 }),
1457 }
1458 }
1459
1460 fn is_soft(&self, c: Constraint<'_>) -> Result<bool, BuildError> {
1461 for tree in self.trees {
1462 if let Some(node) = c.syntax(tree) {
1463 return Ok(
1464 ExpressionConstraintSyntax::cast(node).is_some_and(|s| s.soft().is_some())
1465 );
1466 }
1467 }
1468 Err(BuildError::Missing {
1469 what: "syntax node for a constraint (is its syntax tree passed to the builder?)".into(),
1470 })
1471 }
1472
1473 fn constraint(&mut self, c: Constraint<'_>) -> Result<Vec<Expr>, BuildError> {
1474 fn kids<'d>(c: Constraint<'d>) -> Vec<Constraint<'d>> {
1475 c.children()
1476 .into_iter()
1477 .filter_map(|n| n.as_constraint())
1478 .collect()
1479 }
1480 match c.kind() {
1481 ConstraintKind::List => {
1482 let mut out = Vec::new();
1483 for k in kids(c) {
1484 out.extend(self.constraint(k)?);
1485 }
1486 Ok(out)
1487 }
1488 ConstraintKind::Expression => {
1489 let soft = self.is_soft(c)?;
1490 let e = c.expr().ok_or_else(|| BuildError::Missing {
1491 what: "expression of an expression constraint".into(),
1492 })?;
1493 if soft
1494 && (real::reads_real(e)
1495 || (e.kind() == ExpressionKind::Dist && real::dist_is_real(e)))
1496 {
1497 return Err(BuildError::BadSoft {
1498 what: "a `soft` constraint over a real value is not supported (soft \
1499 constraints are relaxed over integral variables only)"
1500 .into(),
1501 });
1502 }
1503 let dists_from = self.dists.len();
1504 let built = if e.kind() == ExpressionKind::Dist {
1505 if real::dist_is_real(e) {
1506 self.real_dist(e)?;
1507 return Ok(Vec::new());
1508 }
1509 self.dist(e)?
1510 } else {
1511 if real::reads_real(e) {
1512 let cond = self.real_cond(e)?;
1513 self.real_conds.push(cond);
1514 return Ok(Vec::new());
1515 }
1516 to_bool(self.expr(e)?)
1517 };
1518 if soft {
1519 let dists = dists_from..self.dists.len();
1520 return Ok(vec![self.make_soft(built, dists)?]);
1521 }
1522 Ok(vec![built])
1523 }
1524 ConstraintKind::Implication | ConstraintKind::Conditional
1527 if real::constraint_reads_real(c) =>
1528 {
1529 let cond = self.real_constraint(c)?;
1530 self.real_conds.push(cond);
1531 Ok(Vec::new())
1532 }
1533 ConstraintKind::Implication => {
1534 let p = c.predicate().ok_or_else(|| BuildError::Missing {
1535 what: "predicate of an implication".into(),
1536 })?;
1537 let pred = to_bool(self.expr(p)?);
1538 let mut body = Vec::new();
1539 self.guard_depth += 1;
1540 for k in kids(c) {
1541 match self.constraint(k) {
1542 Ok(b) => body.extend(b),
1543 Err(e) => {
1544 self.guard_depth -= 1;
1545 return Err(e);
1546 }
1547 }
1548 }
1549 self.guard_depth -= 1;
1550 Ok(vec![Expr::Guard(
1553 Box::new(pred),
1554 Box::new(and_all(body)),
1555 Box::new(Expr::Const(Ty::BIT, BigUint::one())),
1556 Ty::BIT,
1557 )])
1558 }
1559 ConstraintKind::Conditional => {
1560 let p = c.predicate().ok_or_else(|| BuildError::Missing {
1561 what: "predicate of an if constraint".into(),
1562 })?;
1563 let pred = to_bool(self.expr(p)?);
1564 let arms = kids(c);
1565 if arms.is_empty() || arms.len() > 2 {
1566 return Err(BuildError::Missing {
1567 what: format!("one or two arms of an if constraint (got {})", arms.len()),
1568 });
1569 }
1570 self.guard_depth += 1;
1571 let arms_built = arms
1572 .iter()
1573 .map(|a| self.constraint(*a).map(and_all))
1574 .collect::<Result<Vec<_>, _>>();
1575 self.guard_depth -= 1;
1576 let mut arms_built = arms_built?.into_iter();
1577 let then_c = arms_built.next().ok_or_else(|| BuildError::Missing {
1578 what: "the then arm of an if constraint".into(),
1579 })?;
1580 let else_c = arms_built
1581 .next()
1582 .unwrap_or_else(|| Expr::Const(Ty::BIT, BigUint::one()));
1583 Ok(vec![Expr::Guard(
1584 Box::new(pred),
1585 Box::new(then_c),
1586 Box::new(else_c),
1587 Ty::BIT,
1588 )])
1589 }
1590 ConstraintKind::Uniqueness => self.unique(c),
1591 ConstraintKind::SolveBefore => {
1592 let split = self.solve_side_len(c)?;
1595 let mut ids = Vec::new();
1596 for k in c.children() {
1597 let e = k.as_expression().ok_or_else(|| BuildError::Missing {
1598 what: "expression operand of `solve ... before`".into(),
1599 })?;
1600 ids.push(self.solve_operand(e)?);
1601 }
1602 if split > ids.len() {
1603 return Err(BuildError::Missing {
1604 what: "operands of `solve ... before` matching its syntax".into(),
1605 });
1606 }
1607 let (first, later) = ids.split_at(split);
1608 for &a in first {
1609 for &b in later {
1610 match (a, b) {
1611 (SolveOperand::Int(a), SolveOperand::Int(b)) => {
1612 self.before.push((a, b));
1613 }
1614 (SolveOperand::Real(a), SolveOperand::Real(b)) => {
1615 self.real_before.push((a, b));
1616 }
1617 (SolveOperand::Size, SolveOperand::Int(_) | SolveOperand::Real(_)) => {}
1625 (SolveOperand::Int(_) | SolveOperand::Real(_), SolveOperand::Size)
1626 | (SolveOperand::Size, SolveOperand::Size) => {
1627 return Err(unsupported(
1628 "`solve ... before` placing a variable or another array size \
1629 before an array `.size()`",
1630 "array sizes are always solved first, as one joint sub-problem",
1631 ));
1632 }
1633 _ => {}
1637 }
1638 }
1639 }
1640 Ok(Vec::new())
1641 }
1642 ConstraintKind::Invalid => Err(BuildError::Missing {
1643 what: "well-formed constraint (the frontend diagnosed an error in it)".into(),
1644 }),
1645 ConstraintKind::DisableSoft => {
1646 self.disable_soft(c)?;
1648 Ok(Vec::new())
1649 }
1650 ConstraintKind::Foreach => self.foreach(c),
1651 other => Err(unsupported(format!("constraint kind {other:?}"), "C2")),
1652 }
1653 }
1654
1655 fn symbol_ref(&mut self, sym: Symbol<'_>, ty: Ty) -> Result<Expr, BuildError> {
1656 if self.frame.is_some()
1659 && sym.kind() == SymbolKind::ClassProperty
1660 && let Some(base) = self.fn_base.clone()
1661 {
1662 return self.based_state(&base, &[sym], ty);
1663 }
1664 let uid = sym.uid();
1665 if let Some(x) = self.pattern_binds.get(&uid) {
1666 return Ok(x.clone());
1667 }
1668 if let Some(&id) = self.var_ix.get(&uid) {
1669 if self.frame.is_some() {
1670 return Err(self.rand_read_in_function(sym.name()));
1671 }
1672 return Ok(Expr::Var(id, self.vars[id as usize].ty));
1673 }
1674 self.state_ref(sym, &[], 0, ty)
1675 }
1676
1677 fn state_ref(
1680 &mut self,
1681 root: Symbol<'_>,
1682 hops: &[Symbol<'_>],
1683 root_bit: u32,
1684 ty: Ty,
1685 ) -> Result<Expr, BuildError> {
1686 let key: Vec<SymbolId> = std::iter::once(root.uid())
1687 .chain(hops.iter().map(|h| h.uid()))
1688 .collect();
1689 let key = (key, root_bit);
1690 if let Some(&id) = self.state_ix.get(&key) {
1691 return Ok(Expr::State(id, self.states[id as usize].ty));
1692 }
1693 let mut name = root.name().to_string();
1694 let mut via = Vec::with_capacity(hops.len());
1695 for h in hops {
1696 name = format!("{name}.{}", h.name());
1697 let resolver =
1698 self.slot_of
1699 .as_ref()
1700 .ok_or_else(|| BuildError::ForeignObjectAccess {
1701 what: format!(
1702 "read of `{name}` (this builder was given no object layout to place \
1703 another object's members)"
1704 ),
1705 })?;
1706 let slot = resolver(*h).map_err(|why| BuildError::Missing {
1707 what: format!("field slot of `{name}`: {why}"),
1708 })?;
1709 via.push(StateHop {
1710 symbol: h.uid(),
1711 slot,
1712 });
1713 }
1714 let id = self.states.len() as u32;
1715 self.states.push(StateInfo {
1716 symbol: root.uid(),
1717 name,
1718 ty,
1719 via,
1720 window: None,
1721 root_bit,
1722 class_of: false,
1723 });
1724 self.state_ix.insert(key, id);
1725 Ok(Expr::State(id, ty))
1726 }
1727
1728 fn local_handle_root<'d>(mut e: Expression<'d>) -> Option<Expression<'d>> {
1731 loop {
1734 let kind = e.kind();
1735 if kind == ExpressionKind::MemberAccess {
1736 e = e.left()?;
1737 } else if kind == ExpressionKind::Conversion {
1738 e = e.conversion_operand()?;
1739 } else if kind == ExpressionKind::NamedValue
1740 || kind == ExpressionKind::HierarchicalValue
1741 {
1742 return e
1743 .referenced_symbol()
1744 .is_some_and(|s| s.name() != "this")
1745 .then_some(e);
1746 } else {
1747 return None;
1748 }
1749 }
1750 }
1751
1752 fn handle_path<'d>(e: Expression<'d>) -> Result<Vec<Symbol<'d>>, BuildError> {
1755 match e.kind() {
1756 ExpressionKind::NamedValue | ExpressionKind::HierarchicalValue => {
1757 let sym = e.referenced_symbol().ok_or_else(|| BuildError::Missing {
1758 what: "symbol of a named value".into(),
1759 })?;
1760 Ok(if sym.name() == "this" {
1761 Vec::new()
1762 } else {
1763 vec![sym]
1764 })
1765 }
1766 ExpressionKind::MemberAccess => {
1767 let mut path = Self::handle_path(Self::operand(e, "a member access")?)?;
1768 path.push(e.member_symbol().ok_or_else(|| BuildError::Missing {
1769 what: "member symbol of a member access".into(),
1770 })?);
1771 Ok(path)
1772 }
1773 ExpressionKind::Conversion => {
1774 let inner = e.conversion_operand().ok_or_else(|| BuildError::Missing {
1775 what: "operand of a conversion".into(),
1776 })?;
1777 Self::handle_path(inner)
1778 }
1779 ExpressionKind::ElementSelect => {
1782 let arr = e.select_value().ok_or_else(|| BuildError::Missing {
1783 what: "array of an element select".into(),
1784 })?;
1785 match arr.kind() {
1786 ExpressionKind::NamedValue | ExpressionKind::HierarchicalValue => {
1787 Self::handle_path(arr)
1788 }
1789 other => Err(unsupported(
1790 format!(
1791 "an element of a `{other:?}` array of handles as the object of a member access"
1792 ),
1793 "C17 follow-up",
1794 )),
1795 }
1796 }
1797 other => Err(unsupported(
1798 format!("a `{other:?}` expression as the object of a member access"),
1799 "C17 follow-up",
1800 )),
1801 }
1802 }
1803
1804 fn handle_root_element(&mut self, e: Expression<'_>) -> Result<Option<(u32, u32)>, BuildError> {
1810 let mut cur = e;
1811 loop {
1814 let kind = cur.kind();
1815 if kind == ExpressionKind::MemberAccess {
1816 cur = Self::operand(cur, "a member access")?;
1817 } else if kind == ExpressionKind::Conversion {
1818 cur = cur
1819 .conversion_operand()
1820 .ok_or_else(|| BuildError::Missing {
1821 what: "operand of a conversion".into(),
1822 })?;
1823 } else if kind == ExpressionKind::ElementSelect {
1824 break;
1825 } else {
1826 return Ok(None);
1827 }
1828 }
1829 let arr = cur.select_value().ok_or_else(|| BuildError::Missing {
1830 what: "array of an element select".into(),
1831 })?;
1832 let at = arr
1833 .expr_type()
1834 .map(|t| t.canonical())
1835 .ok_or_else(|| BuildError::Missing {
1836 what: "type of an array of handles".into(),
1837 })?;
1838 if !at.is_unpacked_array() || at.fixed_unpacked_array_range().width() == 0 {
1839 return Err(unsupported(
1840 "an element of a dynamic array, queue or associative array of handles in a constraint",
1841 "C17 follow-up",
1842 ));
1843 }
1844 let sel = cur.selector().ok_or_else(|| BuildError::Missing {
1845 what: "index of an element select".into(),
1846 })?;
1847 let ix = self.expr(sel)?;
1848 let i = Self::constant_index(&ix).ok_or_else(|| {
1849 unsupported(
1850 "an element of an array of handles at a computed index",
1851 "C17 follow-up",
1852 )
1853 })?;
1854 let r = at.fixed_unpacked_array_range();
1855 let lo = i64::from(r.left).min(i64::from(r.right));
1856 let n = i64::from(r.width());
1857 if !(0..n).contains(&(i - lo)) {
1858 return Err(BuildError::OutOfRangeSelect {
1859 what: format!("handle array index {i} outside [{}:{}]", r.left, r.right),
1860 });
1861 }
1862 Ok(Some((((i - lo) * 64) as u32, (n * 64) as u32)))
1863 }
1864
1865 fn handle_state(&mut self, e: Expression<'_>, ty: Ty) -> Result<Expr, BuildError> {
1869 let path = Self::handle_path(e)?;
1870 if path.is_empty() {
1871 return Err(unsupported("a bare `this` as a value", "C17 follow-up"));
1872 }
1873 if self.frame.is_some()
1874 && let Some(base) = self.fn_base.clone()
1875 {
1876 return self.based_state(&base, &path, ty);
1879 }
1880 let Some((root, hops)) = path.split_first() else {
1881 return Err(unsupported("a bare `this` as a value", "C17 follow-up"));
1882 };
1883 if self.var_ix.contains_key(&root.uid()) {
1884 return Err(unsupported(
1885 format!("the randomized handle `{}` in a constraint", root.name()),
1886 "C17 follow-up",
1887 ));
1888 }
1889 let elem = self.handle_root_element(e)?;
1892 for (i, step) in path[..path.len() - 1].iter().enumerate() {
1893 let is_handle = (i == 0 && elem.is_some())
1894 || step.value_type().is_some_and(|t| t.canonical().is_class());
1895 if !is_handle {
1896 return Err(unsupported(
1897 format!(
1898 "a member access through `{}` (step {i}), which is not a class handle",
1899 step.name()
1900 ),
1901 "C17 follow-up",
1902 ));
1903 }
1904 }
1905 let bit = elem.map_or(0, |(b, _)| b);
1906 let out = self.state_ref(*root, hops, bit, ty)?;
1907 if let (Some((bit, whole)), true, Expr::State(id, _)) = (elem, hops.is_empty(), &out) {
1908 self.states[*id as usize].window = Some(Window {
1910 bit,
1911 width: 64,
1912 field_width: whole,
1913 });
1914 }
1915 Ok(out)
1916 }
1917
1918 fn handle_value(&mut self, e: Expression<'_>) -> Result<Expr, BuildError> {
1921 match e.kind() {
1922 ExpressionKind::NullLiteral => Ok(Expr::Const(HANDLE_TY, BigUint::zero())),
1923 ExpressionKind::Conversion => {
1926 let inner = e.conversion_operand().ok_or_else(|| BuildError::Missing {
1927 what: "operand of a conversion".into(),
1928 })?;
1929 self.handle_value(inner)
1930 }
1931 ExpressionKind::NamedValue
1932 | ExpressionKind::HierarchicalValue
1933 | ExpressionKind::ElementSelect
1934 | ExpressionKind::MemberAccess => self.handle_state(e, HANDLE_TY),
1935 other => Err(unsupported(
1936 format!("a `{other:?}` class-handle expression"),
1937 "C17 follow-up",
1938 )),
1939 }
1940 }
1941
1942 pub fn build_expr(&mut self, e: Expression<'_>) -> Result<Expr, BuildError> {
1945 let out = self.expr(e)?;
1946 out.validate()?;
1947 Ok(out)
1948 }
1949
1950 fn operand<'d>(e: Expression<'d>, what: &str) -> Result<Expression<'d>, BuildError> {
1951 e.left().ok_or_else(|| BuildError::Missing {
1952 what: format!("operand of {what}"),
1953 })
1954 }
1955
1956 fn expr(&mut self, e: Expression<'_>) -> Result<Expr, BuildError> {
1957 let kind = e.kind();
1958 if is_handle_type(e.expr_type()) {
1959 return self.handle_value(e);
1960 }
1961 let ty = ty_of(e.expr_type(), &format!("a `{kind:?}` expression"))?;
1962 let pattern_cond = kind == ExpressionKind::ConditionalOp
1966 && e.conditions().iter().any(|c| c.pattern().is_some());
1967 if !pattern_cond
1968 && let Some(cv) = e.constant_value()
1969 && let Some(i) = cv.as_integer()
1970 {
1971 return Ok(Expr::Const(ty, svint_to_biguint(i, ty)?));
1972 }
1973 match kind {
1974 ExpressionKind::NamedValue | ExpressionKind::HierarchicalValue => {
1975 let sym = e.referenced_symbol().ok_or_else(|| BuildError::Missing {
1976 what: "symbol of a named value".into(),
1977 })?;
1978 if let Some(local) = self.frame_var(sym) {
1979 return Ok(local);
1980 }
1981 if sym.kind() == SymbolKind::Iterator {
1982 self.not_in_function("an iterator variable")?;
1983 return self.loop_ref(sym, ty);
1984 }
1985 if self.is_call_local(e) {
1986 self.not_in_function("a local of an inline constraint's call site")?;
1987 return Ok(self.capture_ref(sym, ty));
1988 }
1989 self.symbol_ref(sym, ty)
1990 }
1991 ExpressionKind::MemberAccess => {
1992 if let Some(chain) = aggregate::aggregate_chain(e, &self.forced_roots)
1995 && !chain.steps.is_empty()
1996 {
1997 return self.aggregate_value(chain, ty);
1998 }
1999 let base = Self::operand(e, "a member access")?;
2000 let is_this = base.kind() == ExpressionKind::NamedValue
2001 && base.referenced_symbol().is_some_and(|s| s.name() == "this");
2002 let member = e.member_symbol().ok_or_else(|| BuildError::Missing {
2003 what: "member symbol of a member access".into(),
2004 })?;
2005 if !is_this {
2006 if is_handle_type(base.expr_type()) {
2007 if let Some(root) = Self::local_handle_root(base)
2010 && self.is_call_local(root)
2011 {
2012 self.not_in_function("a local of an inline constraint's call site")?;
2013 let path = Self::handle_path(e)?;
2014 let (r, hops) =
2015 path.split_first().ok_or_else(|| BuildError::Missing {
2016 what: "handle path of a member read".into(),
2017 })?;
2018 return Ok(self.capture_path(*r, hops, ty));
2019 }
2020 return self.handle_state(e, ty);
2022 }
2023 let base_ty = base.expr_type();
2026 if !base_ty.is_some_and(|t| t.is_integral() && (t.is_struct() || t.is_union()))
2027 {
2028 let what = format!("member access `.{}`", member.name());
2029 return Err(unsupported(
2030 format!(
2031 "{what} on a value that is neither a class handle, a packed \
2032 struct or union, nor a member of an unpacked struct property \
2033 of this class"
2034 ),
2035 "C14",
2036 ));
2037 }
2038 let whole = self.expr(base)?;
2039 let low = u32::try_from(member.field_bit_offset()).map_err(|_| {
2040 BuildError::Missing {
2041 what: format!("bit offset of packed member `{}`", member.name()),
2042 }
2043 })?;
2044 let slice = Expr::Slice(Box::new(whole), low, Ty::unsigned(ty.width));
2045 return Ok(if slice.ty() == ty {
2046 slice
2047 } else {
2048 Expr::Resize(Box::new(slice), ty)
2049 });
2050 }
2051 self.symbol_ref(member, ty)
2052 }
2053 ExpressionKind::UnaryOp => {
2054 let op = e.unary_op().ok_or_else(|| BuildError::Missing {
2055 what: "operator of a unary expression".into(),
2056 })?;
2057 let a = self.expr(Self::operand(e, "a unary operator")?)?;
2058 let (uop, rty) = match op {
2059 UnaryOp::Plus => return Ok(a),
2060 UnaryOp::Minus => (UnOp::Neg, ty),
2061 UnaryOp::BitwiseNot => (UnOp::BitNot, ty),
2062 UnaryOp::BitwiseAnd => (UnOp::RedAnd, Ty::BIT),
2063 UnaryOp::BitwiseOr => (UnOp::RedOr, Ty::BIT),
2064 UnaryOp::BitwiseXor => (UnOp::RedXor, Ty::BIT),
2065 UnaryOp::BitwiseNand => (UnOp::RedNand, Ty::BIT),
2066 UnaryOp::BitwiseNor => (UnOp::RedNor, Ty::BIT),
2067 UnaryOp::BitwiseXnor => (UnOp::RedXnor, Ty::BIT),
2068 UnaryOp::LogicalNot => (UnOp::LogNot, Ty::BIT),
2069 other => {
2070 return Err(unsupported(
2071 format!("the side-effecting operator {other:?}"),
2072 "C2",
2073 ));
2074 }
2075 };
2076 Ok(Expr::Unary(uop, Box::new(a), rty))
2077 }
2078 ExpressionKind::BinaryOp => self.binary(e, ty),
2079 ExpressionKind::ConditionalOp => {
2080 let mut bound = Vec::new();
2081 let r = self.conditional(e, ty, &mut bound);
2082 for uid in bound {
2083 self.pattern_binds.remove(&uid);
2084 }
2085 r
2086 }
2087 ExpressionKind::Conversion => self.conversion(e, ty),
2088 ExpressionKind::Concatenation => {
2089 let mut parts = Vec::new();
2090 let mut w = 0u32;
2091 for o in e.operands() {
2092 let p = self.expr(o)?;
2093 w += p.ty().width;
2094 parts.push(p);
2095 }
2096 Ok(Expr::Concat(parts, Ty::unsigned(w)))
2097 }
2098 ExpressionKind::Replication => {
2099 let n = e
2100 .replication_count()
2101 .and_then(|c| c.constant_value())
2102 .and_then(|c| c.as_i64())
2103 .ok_or_else(|| BuildError::Missing {
2104 what: "constant count of a replication".into(),
2105 })?;
2106 if n < 1 {
2107 return Err(unsupported("a replication with a count below 1", "C2"));
2108 }
2109 let inner =
2110 self.expr(e.replication_concat().ok_or_else(|| BuildError::Missing {
2111 what: "operand of a replication".into(),
2112 })?)?;
2113 let w = inner.ty().width * n as u32;
2114 Ok(Expr::Concat(vec![inner; n as usize], Ty::unsigned(w)))
2115 }
2116 ExpressionKind::Inside => self.inside(e),
2117 ExpressionKind::ElementSelect => self.element_select(e, ty),
2118 ExpressionKind::RangeSelect => self.range_select(e, ty),
2119 ExpressionKind::Call => {
2120 if e.is_system_call() == Some(true)
2121 && self.frame.is_none()
2122 && let Some(r) = self.array_call(e, ty)
2123 {
2124 return r;
2125 }
2126 let name = e.call_subroutine_name().unwrap_or_default();
2127 if e.is_system_call() == Some(true) && (name == "$signed" || name == "$unsigned") {
2128 let args = e.operands();
2129 let [arg] = args.as_slice() else {
2130 return Err(BuildError::Missing {
2131 what: format!("single argument of {name}"),
2132 });
2133 };
2134 let a = self.expr(*arg)?;
2135 return Ok(Expr::Resize(Box::new(a), ty));
2136 }
2137 if e.is_system_call() == Some(true)
2138 && let Some(r) = self.bit_count_call(e, ty)
2139 {
2140 return r;
2141 }
2142 if e.is_system_call() == Some(true) {
2143 return Err(unsupported(format!("a call to `{name}`"), "C13"));
2144 }
2145 self.user_call(e, ty)
2146 }
2147 ExpressionKind::LValueReference => self.lvalue_reference(),
2148 ExpressionKind::Dist => Err(BuildError::BadDist {
2149 what: "a `dist` nested inside another expression (it is a constraint item, \
2150 never an operand)"
2151 .into(),
2152 }),
2153 ExpressionKind::UnboundedLiteral => Err(unsupported(
2154 "an unbounded `$` outside an `inside` range",
2155 "C2",
2156 )),
2157 other => Err(unsupported(format!("a `{other:?}` expression"), "C2")),
2158 }
2159 }
2160
2161 fn conditional(
2167 &mut self,
2168 e: Expression<'_>,
2169 ty: Ty,
2170 bound: &mut Vec<SymbolId>,
2171 ) -> Result<Expr, BuildError> {
2172 let mut tests = Vec::new();
2173 for c in e.conditions() {
2174 let subject = self.expr(c.expr())?;
2175 tests.push(match c.pattern() {
2176 None => to_bool(subject),
2177 Some(p) => self.pattern_test(&p, subject, bound)?,
2178 });
2179 }
2180 let t = self.expr(e.true_value().ok_or_else(|| BuildError::Missing {
2181 what: "true arm of a conditional".into(),
2182 })?)?;
2183 for uid in bound.drain(..) {
2185 self.pattern_binds.remove(&uid);
2186 }
2187 let f = self.expr(e.false_value().ok_or_else(|| BuildError::Missing {
2188 what: "false arm of a conditional".into(),
2189 })?)?;
2190 Ok(Expr::Ite(
2191 Box::new(and_all(tests)),
2192 Box::new(t),
2193 Box::new(f),
2194 ty,
2195 ))
2196 }
2197
2198 fn pattern_test(
2205 &mut self,
2206 pat: &sv_lang::Pattern<'_>,
2207 subject: Expr,
2208 bound: &mut Vec<SymbolId>,
2209 ) -> Result<Expr, BuildError> {
2210 match pat.kind() {
2211 PatternKind::Constant => {
2212 let v = self.expr(pat.value_expr().ok_or_else(|| BuildError::Missing {
2213 what: "value of a constant pattern".into(),
2214 })?)?;
2215 let (a, b) = (subject.ty(), v.ty());
2216 let ct = Ty {
2217 width: a.width.max(b.width),
2218 signed: a.signed && b.signed,
2219 };
2220 Ok(bin(BinOp::Eq, coerce(subject, ct), coerce(v, ct), Ty::BIT))
2221 }
2222 PatternKind::Wildcard => Ok(Expr::Const(Ty::BIT, BigUint::one())),
2223 PatternKind::Variable => {
2224 if let Some(var) = pat.variable()
2225 && !var.name().is_empty()
2226 {
2227 let uid = var.uid();
2228 self.pattern_binds.insert(uid.clone(), subject);
2229 bound.push(uid);
2230 }
2231 Ok(Expr::Const(Ty::BIT, BigUint::one()))
2232 }
2233 other => Err(unsupported(
2234 format!(
2235 "a {other:?} pattern in a constraint (it matches a tagged union or an \
2236 unpacked struct, which is never a random variable)"
2237 ),
2238 "C2",
2239 )),
2240 }
2241 }
2242
2243 fn binary(&mut self, e: Expression<'_>, ty: Ty) -> Result<Expr, BuildError> {
2244 let op = e.binary_op().ok_or_else(|| BuildError::Missing {
2245 what: "operator of a binary expression".into(),
2246 })?;
2247 let (lhs, rhs) = match (e.left(), e.right()) {
2248 (Some(l), Some(r)) => (l, r),
2249 _ => {
2250 return Err(BuildError::Missing {
2251 what: "operands of a binary expression".into(),
2252 });
2253 }
2254 };
2255 let (bop, rty) = match op {
2256 BinaryOp::CaseEquality => return Err(BuildError::FourStateOperator { op: "===" }),
2257 BinaryOp::CaseInequality => return Err(BuildError::FourStateOperator { op: "!==" }),
2258 BinaryOp::WildcardEquality => return Err(BuildError::FourStateOperator { op: "==?" }),
2259 BinaryOp::WildcardInequality => {
2260 return Err(BuildError::FourStateOperator { op: "!=?" });
2261 }
2262 BinaryOp::Power => {
2263 let l = self.expr(lhs)?;
2264 let r = self.expr(rhs)?;
2265 return Ok(power(l, r, ty));
2266 }
2267 BinaryOp::Add => (BinOp::Add, ty),
2268 BinaryOp::Subtract => (BinOp::Sub, ty),
2269 BinaryOp::Multiply => (BinOp::Mul, ty),
2270 BinaryOp::Divide => (BinOp::Div, ty),
2271 BinaryOp::Mod => (BinOp::Mod, ty),
2272 BinaryOp::BinaryAnd => (BinOp::And, ty),
2273 BinaryOp::BinaryOr => (BinOp::Or, ty),
2274 BinaryOp::BinaryXor => (BinOp::Xor, ty),
2275 BinaryOp::BinaryXnor => (BinOp::Xnor, ty),
2276 BinaryOp::Equality => (BinOp::Eq, Ty::BIT),
2277 BinaryOp::Inequality => (BinOp::Ne, Ty::BIT),
2278 BinaryOp::GreaterThanEqual => (BinOp::Ge, Ty::BIT),
2279 BinaryOp::GreaterThan => (BinOp::Gt, Ty::BIT),
2280 BinaryOp::LessThanEqual => (BinOp::Le, Ty::BIT),
2281 BinaryOp::LessThan => (BinOp::Lt, Ty::BIT),
2282 BinaryOp::LogicalAnd => (BinOp::LogAnd, Ty::BIT),
2283 BinaryOp::LogicalOr => (BinOp::LogOr, Ty::BIT),
2284 BinaryOp::LogicalImplication => (BinOp::LogImpl, Ty::BIT),
2285 BinaryOp::LogicalEquivalence => (BinOp::LogEquiv, Ty::BIT),
2286 BinaryOp::LogicalShiftLeft | BinaryOp::ArithmeticShiftLeft => (BinOp::Shl, ty),
2287 BinaryOp::LogicalShiftRight => (BinOp::Shr, ty),
2288 BinaryOp::ArithmeticShiftRight => (BinOp::Ashr, ty),
2289 };
2290 let whole = |t: Option<Type<'_>>| {
2296 t.is_some_and(|t| {
2297 let c = t.canonical();
2298 c.is_unpacked_array() || c.is_unpacked_struct()
2299 })
2300 };
2301 if matches!(bop, BinOp::Eq | BinOp::Ne) && whole(lhs.expr_type()) && whole(rhs.expr_type())
2302 {
2303 let chains = aggregate::aggregate_chain(lhs, &self.forced_roots)
2304 .zip(aggregate::aggregate_chain(rhs, &self.forced_roots));
2305 let Some((lc, rc)) = chains else {
2306 return Err(unsupported(
2307 "an array comparison whose operand is not a property (or a member of a \
2308 struct property) of this class",
2309 "C14",
2310 ));
2311 };
2312 let l = self.aggregate_bitstream(lc)?;
2313 let r = self.aggregate_bitstream(rc)?;
2314 return Ok(bin(bop, l, r, rty));
2315 }
2316 let is_string =
2320 |e: Expression<'_>| e.expr_type().is_some_and(|t| t.canonical().is_string());
2321 if is_string(lhs) || is_string(rhs) {
2322 if !matches!(bop, BinOp::Eq | BinOp::Ne) {
2323 return Err(unsupported(
2324 format!("the string operator {op:?} (only `==` and `!=` compare strings)"),
2325 "C13",
2326 ));
2327 }
2328 let l = self.string_id(lhs)?;
2329 let r = self.string_id(rhs)?;
2330 return Ok(bin(bop, l, r, rty));
2331 }
2332 let l = self.expr(lhs)?;
2333 let r = self.expr(rhs)?;
2334 Ok(bin(bop, l, r, rty))
2335 }
2336
2337 fn string_id(&mut self, e: Expression<'_>) -> Result<Expr, BuildError> {
2341 if let Some(cv) = e.constant_value()
2342 && let Some(s) = cv.as_str()
2343 {
2344 let id = crate::strings::intern(s.as_bytes());
2345 return Ok(Expr::Const(HANDLE_TY, BigUint::from(id)));
2346 }
2347 match e.kind() {
2348 ExpressionKind::Conversion => {
2349 let inner = e.conversion_operand().ok_or_else(|| BuildError::Missing {
2350 what: "operand of a conversion".into(),
2351 })?;
2352 self.string_id(inner)
2353 }
2354 ExpressionKind::NamedValue
2355 | ExpressionKind::HierarchicalValue
2356 | ExpressionKind::MemberAccess => {
2357 let sym = e.referenced_symbol().or_else(|| e.member_symbol());
2358 if sym.is_some_and(|s| self.var_ix.contains_key(&s.uid())) {
2359 return Err(unsupported("a `rand` string", "C13"));
2360 }
2361 self.handle_state(e, HANDLE_TY)
2362 }
2363 other => Err(unsupported(
2364 format!("a `{other:?}` string operand of a comparison"),
2365 "C13",
2366 )),
2367 }
2368 }
2369
2370 fn conversion(&mut self, e: Expression<'_>, to: Ty) -> Result<Expr, BuildError> {
2371 let operand = e.conversion_operand().ok_or_else(|| BuildError::Missing {
2372 what: "operand of a conversion".into(),
2373 })?;
2374 let kind = e.conversion_kind().ok_or_else(|| BuildError::Missing {
2375 what: "kind of a conversion".into(),
2376 })?;
2377 if operand.expr_type().is_some_and(|t| !t.is_integral())
2381 && let Some(chain) = aggregate::aggregate_chain(operand, &self.forced_roots)
2382 {
2383 let x = self.aggregate_bitstream(chain)?;
2384 return Ok(if x.ty() == to {
2385 x
2386 } else {
2387 Expr::Resize(Box::new(x), to)
2388 });
2389 }
2390 let x = self.expr(operand)?;
2391 match kind {
2392 1 => Ok(coerce(x, to)),
2394 0 | 3 => Ok(if x.ty() == to {
2396 x
2397 } else {
2398 Expr::Resize(Box::new(x), to)
2399 }),
2400 other => Err(unsupported(
2401 format!("a conversion of kind {other} (streaming or bit-stream cast)"),
2402 "C2",
2403 )),
2404 }
2405 }
2406
2407 fn range_op(&self, item: Expression<'_>) -> Result<RangeOp, BuildError> {
2410 for tree in self.trees {
2411 if let Some(node) = item.syntax(tree) {
2412 let r =
2413 ValueRangeExpressionSyntax::cast(node).ok_or_else(|| BuildError::Missing {
2414 what: "value-range syntax for a ValueRange expression".into(),
2415 })?;
2416 let op = r.op().ok_or_else(|| BuildError::Missing {
2417 what: "operator of a value range".into(),
2418 })?;
2419 return match op.text() {
2420 ":" => Ok(RangeOp::Span),
2421 "+/-" => Ok(RangeOp::Abs),
2422 "+%-" => Ok(RangeOp::Pct),
2423 other => Err(unsupported(
2424 format!("the value-range operator `{other}`"),
2425 "C7",
2426 )),
2427 };
2428 }
2429 }
2430 Err(BuildError::Missing {
2431 what: "syntax node for a value range (is its syntax tree passed to the builder?)"
2432 .into(),
2433 })
2434 }
2435
2436 fn value_range(&mut self, item: Expression<'_>, subject: Ty) -> Result<Bounds, BuildError> {
2440 let (l, r) = match (item.left(), item.right()) {
2441 (Some(l), Some(r)) => (l, r),
2442 _ => {
2443 return Err(BuildError::Missing {
2444 what: "bounds of a value range".into(),
2445 });
2446 }
2447 };
2448 let op = self.range_op(item)?;
2449 let is_unbounded = |b: Expression<'_>| {
2451 let mut peeled = b;
2452 while peeled.kind() == ExpressionKind::Conversion {
2453 match peeled.conversion_operand() {
2454 Some(inner) => peeled = inner,
2455 None => break,
2456 }
2457 }
2458 peeled.kind() == ExpressionKind::UnboundedLiteral
2459 };
2460 if op == RangeOp::Span {
2461 let mut side = |b: Expression<'_>| -> Result<Option<(Ty, Expr)>, BuildError> {
2462 if is_unbounded(b) {
2463 return Ok(None);
2464 }
2465 let bx = self.expr(b)?;
2466 let t = common_ty(subject, bx.ty());
2467 Ok(Some((t, coerce(bx, t))))
2468 };
2469 let lo = side(l)?;
2470 let hi = side(r)?;
2471 return Ok(Bounds { lo, hi });
2472 }
2473 if is_unbounded(l) || is_unbounded(r) {
2474 return Err(BuildError::BadDist {
2475 what: "`$` as an operand of a `+/-` or `+%-` range".into(),
2476 });
2477 }
2478 let a = self.expr(l)?;
2479 let b = self.expr(r)?;
2480 let wide = Ty::signed(2 * subject.width.max(a.ty().width).max(b.ty().width) + 8);
2481 let (a, b) = (coerce(a, wide), coerce(b, wide));
2482 let delta = match op {
2483 RangeOp::Abs => b,
2484 _ => {
2485 let hundred = iconst(wide, 100);
2486 bin(
2487 BinOp::Div,
2488 bin(BinOp::Mul, a.clone(), b, wide),
2489 hundred,
2490 wide,
2491 )
2492 }
2493 };
2494 let lo = bin(BinOp::Sub, a.clone(), delta.clone(), wide);
2495 let hi = bin(BinOp::Add, a, delta, wide);
2496 Ok(Bounds {
2497 lo: Some((wide, lo)),
2498 hi: Some((wide, hi)),
2499 })
2500 }
2501
2502 fn dist(&mut self, e: Expression<'_>) -> Result<Expr, BuildError> {
2506 let runtime = self.in_runtime_loop();
2510 let saved = if runtime {
2511 self.no_arrays
2512 } else {
2513 self.no_arrays.replace("a `dist` constraint")
2514 };
2515 let r = self.dist_item(e, runtime);
2516 self.no_arrays = saved;
2517 r
2518 }
2519
2520 fn dist_item(&mut self, e: Expression<'_>, runtime: bool) -> Result<Expr, BuildError> {
2521 let bad = |what: &str| BuildError::BadDist { what: what.into() };
2522 let left = e.dist_left().ok_or_else(|| BuildError::Missing {
2523 what: "distributed expression of a `dist`".into(),
2524 })?;
2525 let mut left = left;
2530 while left.kind() == ExpressionKind::Conversion {
2531 match left.conversion_operand() {
2532 Some(inner) => left = inner,
2533 None => break,
2534 }
2535 }
2536 let subject = self.expr(left)?;
2537 let mut vars: Vec<VarId> = Vec::new();
2538 let mut randc: Option<VarId> = None;
2539 subject.for_each_var(&mut |v, _| {
2540 vars.push(v);
2541 if self.vars[v as usize].randc {
2542 randc = Some(v);
2543 }
2544 });
2545 vars.sort_unstable();
2546 vars.dedup();
2547 if let Some(v) = randc {
2548 return Err(BuildError::BadDist {
2549 what: format!(
2550 "`randc` variable `{}` in a `dist` (randc variables take no weights)",
2551 self.vars[v as usize].name
2552 ),
2553 });
2554 }
2555 let plain_var = match &subject {
2556 Expr::Var(v, _) => Some(*v),
2557 _ => None,
2558 };
2559 let sty = subject.ty();
2560 let state_only = |x: &Expr, what: &str| -> Result<(), BuildError> {
2561 let mut has_var = false;
2562 x.for_each_var(&mut |_, _| has_var = true);
2563 if has_var {
2564 return Err(BuildError::BadDist {
2565 what: format!(
2566 "a random variable in {what}; weights and bounds are fixed values"
2567 ),
2568 });
2569 }
2570 Ok(())
2571 };
2572 let weight_of = |this: &mut Self,
2573 w: Option<sv_lang::DistWeight<'_>>|
2574 -> Result<(Expr, bool), BuildError> {
2575 match w {
2576 None => Ok((Expr::constant(Ty::unsigned(32), 1u32), false)),
2578 Some(w) => {
2579 let x = this.expr(w.expr)?;
2580 state_only(&x, "a `dist` weight")?;
2581 Ok((x, w.kind == DistWeightKind::PerRange))
2582 }
2583 }
2584 };
2585 let mut items = Vec::new();
2586 for it in e.dist_items() {
2587 let v = it.value();
2588 let (member, ty, span) = if v.kind() == ExpressionKind::ValueRange {
2589 let b = self.value_range(v, sty)?;
2590 let ty = match (&b.lo, &b.hi) {
2593 (Some((t, _)), _) | (_, Some((t, _))) => *t,
2594 (None, None) => sty,
2595 };
2596 let mut conj = Vec::new();
2597 for (bound, cmp) in [(&b.lo, BinOp::Ge), (&b.hi, BinOp::Le)] {
2598 if let Some((t, x)) = bound {
2599 state_only(x, "a `dist` range bound")?;
2600 conj.push(bin(cmp, coerce(subject.clone(), *t), x.clone(), Ty::BIT));
2601 }
2602 }
2603 (
2604 and_all(conj),
2605 ty,
2606 Span::Range {
2607 lo: b.lo.map(|(_, x)| x),
2608 hi: b.hi.map(|(_, x)| x),
2609 },
2610 )
2611 } else {
2612 let x = self.expr(v)?;
2613 state_only(&x, "a `dist` value")?;
2614 let t = common_ty(sty, x.ty());
2615 let x = coerce(x, t);
2616 let m = bin(BinOp::Eq, coerce(subject.clone(), t), x.clone(), Ty::BIT);
2617 (m, t, Span::Point(x))
2618 };
2619 let (w, per_range) = weight_of(self, it.weight())?;
2620 items.push(Item {
2621 member,
2622 ty,
2623 span,
2624 weight: w,
2625 per_range,
2626 });
2627 }
2628 let default = match e.dist_default_weight() {
2629 Some(w) => {
2630 let (weight, per_range) = weight_of(self, Some(w))?;
2631 Some(DefaultWeight { weight, per_range })
2632 }
2633 None => None,
2634 };
2635 if items.is_empty() && default.is_none() {
2636 return Err(bad("a `dist` with no items"));
2637 }
2638 let d = Dist {
2639 subject_ty: sty,
2640 plain_var,
2641 vars,
2642 items,
2643 default,
2644 subject: runtime.then_some(subject),
2645 };
2646 let sel = SELECTOR_BASE + self.dists.len() as u32;
2647 let conjunct = d.conjunct(sel);
2648 self.dists.push(d);
2649 Ok(conjunct)
2650 }
2651
2652 fn inside(&mut self, e: Expression<'_>) -> Result<Expr, BuildError> {
2653 let left = self.expr(e.inside_left().ok_or_else(|| BuildError::Missing {
2654 what: "left operand of `inside`".into(),
2655 })?)?;
2656 let items = e.inside_range_list();
2657 if items.is_empty() {
2658 return Err(BuildError::Missing {
2659 what: "candidates of `inside`".into(),
2660 });
2661 }
2662 let mut acc: Option<Expr> = None;
2667 for item in items {
2668 let term = if item.kind() == ExpressionKind::ValueRange {
2669 let b = self.value_range(item, left.ty())?;
2672 let mut conj: Vec<Expr> = Vec::new();
2673 for (bound, cmp) in [(b.lo, BinOp::Ge), (b.hi, BinOp::Le)] {
2674 if let Some((t, x)) = bound {
2675 conj.push(bin(cmp, coerce(left.clone(), t), x, Ty::BIT));
2676 }
2677 }
2678 and_all(conj)
2679 } else if item
2680 .expr_type()
2681 .is_some_and(|t| arrays::array_shape(t).is_some())
2682 {
2683 self.inside_array(&left, item)?
2684 } else {
2685 let ix = self.expr(item)?;
2686 let t = common_ty(left.ty(), ix.ty());
2687 bin(BinOp::Eq, coerce(left.clone(), t), coerce(ix, t), Ty::BIT)
2688 };
2689 acc = Some(match acc {
2690 None => term,
2691 Some(prev) => bin(BinOp::LogOr, prev, term, Ty::BIT),
2692 });
2693 }
2694 Ok(acc.expect("candidates non-empty, checked above"))
2695 }
2696
2697 fn select_base(&mut self, e: Expression<'_>) -> Result<(Expr, Geom), BuildError> {
2699 let base = e.select_value().ok_or_else(|| BuildError::Missing {
2700 what: "base of a select".into(),
2701 })?;
2702 let bt = base.expr_type().ok_or_else(|| BuildError::Missing {
2703 what: "type of a select base".into(),
2704 })?;
2705 let bx = self.expr(base)?;
2706 let geom = Geom::of(bt, bx.ty().width, "a select base")?;
2707 Ok((bx, geom))
2708 }
2709
2710 fn wide_index(&mut self, idx: Expression<'_>) -> Result<(Expr, Ty), BuildError> {
2713 let ix = self.expr(idx)?;
2714 let wide = Ty::signed(ix.ty().width.max(32) + 2);
2715 Ok((Expr::Resize(Box::new(ix), wide), wide))
2716 }
2717
2718 fn dyn_window(base: Expr, pos: Expr, wide: Ty, ew: u32, out_width: u32, ty: Ty) -> Expr {
2720 let off = if ew > 1 {
2721 bin(BinOp::Mul, pos, iconst(wide, i64::from(ew)), wide)
2722 } else {
2723 pos
2724 };
2725 let bty = base.ty();
2726 let shifted = bin(BinOp::Shr, base, off, bty);
2727 Expr::Resize(
2728 Box::new(Expr::Resize(Box::new(shifted), Ty::unsigned(out_width))),
2729 ty,
2730 )
2731 }
2732
2733 fn static_window(
2734 base: Expr,
2735 geom: &Geom,
2736 lo_idx: i64,
2737 hi_idx: i64,
2738 what: &str,
2739 ty: Ty,
2740 ) -> Result<Expr, BuildError> {
2741 let (pl, ph) = (geom.pos(lo_idx), geom.pos(hi_idx));
2742 let (low, high) = (pl.min(ph), pl.max(ph));
2743 if low < 0 || high >= geom.elems() {
2744 return Err(BuildError::OutOfRangeSelect {
2745 what: format!(
2746 "{what}[{lo_idx}..{hi_idx}] outside [{}:{}]",
2747 geom.left, geom.right
2748 ),
2749 });
2750 }
2751 let width = (high - low + 1) as u32 * geom.ew;
2752 let slice = Expr::Slice(Box::new(base), low as u32 * geom.ew, Ty::unsigned(width));
2753 Ok(if slice.ty() == ty {
2754 slice
2755 } else {
2756 Expr::Resize(Box::new(slice), ty)
2757 })
2758 }
2759
2760 fn element_select(&mut self, e: Expression<'_>, ty: Ty) -> Result<Expr, BuildError> {
2761 if let Some(chain) = aggregate::aggregate_chain(e, &self.forced_roots)
2763 && !chain.steps.is_empty()
2764 {
2765 return self.aggregate_value(chain, ty);
2766 }
2767 if let Some(arr) = e.select_value()
2768 && arr
2769 .expr_type()
2770 .is_some_and(|t| arrays::array_shape(t).is_some())
2771 {
2772 self.not_in_function("an array element")?;
2773 return self.array_element(e, arr, ty);
2774 }
2775 let (base, geom) = self.select_base(e)?;
2776 let sel = e.selector().ok_or_else(|| BuildError::Missing {
2777 what: "index of an element select".into(),
2778 })?;
2779 if let Some(i) = sel.constant_value().and_then(|c| c.as_i64()) {
2780 return Self::static_window(base, &geom, i, i, "element select", ty);
2781 }
2782 let (idx, wide) = self.wide_index(sel)?;
2783 let pos = if geom.descending() {
2784 bin(BinOp::Sub, idx, iconst(wide, geom.right), wide)
2785 } else {
2786 bin(BinOp::Sub, iconst(wide, geom.right), idx, wide)
2787 };
2788 Ok(Self::dyn_window(base, pos, wide, geom.ew, ty.width, ty))
2789 }
2790
2791 fn range_select(&mut self, e: Expression<'_>, ty: Ty) -> Result<Expr, BuildError> {
2792 let (base, geom) = self.select_base(e)?;
2793 let kind = e
2794 .range_selection_kind()
2795 .ok_or_else(|| BuildError::Missing {
2796 what: "kind of a range select".into(),
2797 })?;
2798 let (l, r) = match (e.range_left(), e.range_right()) {
2799 (Some(l), Some(r)) => (l, r),
2800 _ => {
2801 return Err(BuildError::Missing {
2802 what: "bounds of a range select".into(),
2803 });
2804 }
2805 };
2806 let const_of = |x: Expression<'_>| x.constant_value().and_then(|c| c.as_i64());
2807 if kind == 0 {
2808 let (Some(a), Some(b)) = (const_of(l), const_of(r)) else {
2809 return Err(BuildError::Missing {
2810 what: "constant bounds of a `[l:r]` select".into(),
2811 });
2812 };
2813 return Self::static_window(base, &geom, a.min(b), a.max(b), "range select", ty);
2814 }
2815 let w = const_of(r)
2816 .filter(|w| *w >= 1)
2817 .ok_or_else(|| BuildError::Missing {
2818 what: "constant width of an indexed part select".into(),
2819 })?;
2820 let (dlo, dhi) = if kind == 1 { (0, w - 1) } else { (-(w - 1), 0) };
2822 if let Some(b) = const_of(l) {
2823 return Self::static_window(base, &geom, b + dlo, b + dhi, "part select", ty);
2824 }
2825 let (idx, wide) = self.wide_index(l)?;
2826 let pos = if geom.descending() {
2827 bin(BinOp::Add, idx, iconst(wide, dlo - geom.right), wide)
2828 } else {
2829 bin(BinOp::Sub, iconst(wide, geom.right - dhi), idx, wide)
2830 };
2831 Ok(Self::dyn_window(base, pos, wide, geom.ew, ty.width, ty))
2832 }
2833}