1use super::dist::{DefaultWeight, Dist, Item, SELECTOR_BASE, Span};
100use super::func::Func;
101use super::ir::{ArrayNode, 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::Array(_)) | 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 outer in 0..self.arrays.len() as u32 {
1333 let n = self.arrays[outer as usize].inner_fixed;
1334 if n == 0 {
1335 continue;
1336 }
1337 let int = Ty::signed(32);
1338 let depth = self.arrays[outer as usize].row_depth as usize;
1341 let mut ts = vec![outer];
1342 for _ in 0..depth {
1343 let t = self.row_template(*ts.last().expect("one array"));
1344 ts.push(t);
1345 }
1346 let loops: Vec<u32> = (0..depth).map(|_| self.fresh_loop()).collect();
1347 let size = Expr::Array(ArrayNode::Size(ts[depth]));
1348 let mut e = bin(BinOp::Eq, size, Expr::Const(int, BigUint::from(n)), Ty::BIT);
1349 for k in (0..depth).rev() {
1350 e = Expr::Array(ArrayNode::RowScope {
1351 template: ts[k + 1],
1352 row: Box::new(Expr::Array(ArrayNode::Loop(loops[k], int))),
1353 body: Box::new(e),
1354 });
1355 e = Expr::Array(ArrayNode::Foreach {
1356 array: ts[k],
1357 var: loops[k],
1358 body: Box::new(e),
1359 });
1360 }
1361 self.conjuncts.push(e);
1362 }
1363 for c in &self.conjuncts {
1364 c.validate()?;
1365 }
1366 for d in &self.dists {
1367 d.validate()?;
1368 }
1369 order::levels(self.vars.len(), &self.before)?;
1370 let (real_vars, real) = self.finish_real()?;
1371 priority::apply_disables(&mut self.softs, &self.disables);
1372 Ok(ConstraintSet {
1373 vars: self.vars,
1374 states: self.states,
1375 conjuncts: self.conjuncts,
1376 domain_conjunct,
1377 before: self.before,
1378 dists: self.dists,
1379 real_vars,
1380 real,
1381 arrays: self.arrays,
1382 funcs: self.funcs,
1383 softs: self.softs,
1384 captures: self.captures,
1385 blocks: self.blocks,
1386 disables: self.disables.len(),
1387 disable_keys: self.disables.iter().map(|d| d.key).collect(),
1388 })
1389 }
1390
1391 fn solve_side_len(&self, c: Constraint<'_>) -> Result<usize, BuildError> {
1395 for tree in self.trees {
1396 if let Some(node) = c.syntax(tree) {
1397 let s =
1398 SolveBeforeConstraintSyntax::cast(node).ok_or_else(|| BuildError::Missing {
1399 what: "`solve ... before` syntax for a SolveBefore constraint".into(),
1400 })?;
1401 return Ok(s.before_expr().iter().count());
1402 }
1403 }
1404 Err(BuildError::Missing {
1405 what: "syntax node for a constraint (is its syntax tree passed to the builder?)".into(),
1406 })
1407 }
1408
1409 fn solve_operand(&mut self, e: Expression<'_>) -> Result<SolveOperand, BuildError> {
1413 if let Some(chain) = aggregate::aggregate_chain(e, &self.forced_roots)
1416 && !chain.steps.is_empty()
1417 {
1418 let (leaf, declared) = self.declared_leaf(chain, "a `solve ... before` operand")?;
1419 if leaf.randc() {
1420 return Err(BuildError::BadSolveOperand {
1421 what: format!(
1422 "`{}` is `randc`, which is always solved before every `rand` variable",
1423 leaf.name
1424 ),
1425 });
1426 }
1427 return match declared {
1428 Some(Declared::Int(id)) => Ok(SolveOperand::Int(id)),
1429 Some(Declared::Real(id)) => Ok(SolveOperand::Real(id)),
1430 Some(Declared::Array(_)) => Err(BuildError::BadSolveOperand {
1431 what: format!("`{}` is an array, not a scalar random variable", leaf.name),
1432 }),
1433 None => Err(BuildError::BadSolveOperand {
1434 what: format!("`{}` is not a `rand` variable of this class", leaf.name),
1435 }),
1436 };
1437 }
1438 let sym = match e.kind() {
1439 ExpressionKind::NamedValue => e.referenced_symbol(),
1440 ExpressionKind::MemberAccess => {
1441 let base = Self::operand(e, "a member access")?;
1442 let is_this = base.kind() == ExpressionKind::NamedValue
1443 && base.referenced_symbol().is_some_and(|s| s.name() == "this");
1444 if !is_this {
1445 return Err(BuildError::ForeignObjectAccess {
1446 what: format!(
1447 "`solve ... before` operand `.{}`",
1448 e.member_symbol()
1449 .map_or(String::new(), |m| m.name().to_string())
1450 ),
1451 });
1452 }
1453 e.member_symbol()
1454 }
1455 ExpressionKind::Call => {
1456 let Some(arr) = Self::size_call_array(e) else {
1457 return Err(unsupported(
1458 "a call other than `array.size` as a `solve ... before` operand",
1459 "C11",
1460 ));
1461 };
1462 self.arrays_allowed("an array size")?;
1463 self.array_of(arr)?;
1464 return Ok(SolveOperand::Size);
1465 }
1466 other => {
1467 return Err(unsupported(
1468 format!("a `{other:?}` expression as a `solve ... before` operand"),
1469 "C11",
1470 ));
1471 }
1472 };
1473 let sym = sym.ok_or_else(|| BuildError::Missing {
1474 what: "symbol of a `solve ... before` operand".into(),
1475 })?;
1476 let name = sym.name().to_string();
1477 if sym.rand_mode() == RandMode::RandC {
1478 return Err(BuildError::BadSolveOperand {
1479 what: format!(
1480 "`{name}` is `randc`, which is always solved before every `rand` variable"
1481 ),
1482 });
1483 }
1484 let uid = sym.uid();
1485 if let Some(&id) = self.var_ix.get(&uid) {
1486 return Ok(SolveOperand::Int(id));
1487 }
1488 match self.real_var_ix.get(&uid) {
1489 Some(&id) => Ok(SolveOperand::Real(id)),
1490 None => Err(BuildError::BadSolveOperand {
1491 what: format!("`{name}` is not a `rand` variable of this class"),
1492 }),
1493 }
1494 }
1495
1496 fn is_soft(&self, c: Constraint<'_>) -> Result<bool, BuildError> {
1497 for tree in self.trees {
1498 if let Some(node) = c.syntax(tree) {
1499 return Ok(
1500 ExpressionConstraintSyntax::cast(node).is_some_and(|s| s.soft().is_some())
1501 );
1502 }
1503 }
1504 Err(BuildError::Missing {
1505 what: "syntax node for a constraint (is its syntax tree passed to the builder?)".into(),
1506 })
1507 }
1508
1509 fn constraint(&mut self, c: Constraint<'_>) -> Result<Vec<Expr>, BuildError> {
1510 fn kids<'d>(c: Constraint<'d>) -> Vec<Constraint<'d>> {
1511 c.children()
1512 .into_iter()
1513 .filter_map(|n| n.as_constraint())
1514 .collect()
1515 }
1516 match c.kind() {
1517 ConstraintKind::List => {
1518 let mut out = Vec::new();
1519 for k in kids(c) {
1520 out.extend(self.constraint(k)?);
1521 }
1522 Ok(out)
1523 }
1524 ConstraintKind::Expression => {
1525 let soft = self.is_soft(c)?;
1526 let e = c.expr().ok_or_else(|| BuildError::Missing {
1527 what: "expression of an expression constraint".into(),
1528 })?;
1529 if soft
1530 && (real::reads_real(e)
1531 || (e.kind() == ExpressionKind::Dist && real::dist_is_real(e)))
1532 {
1533 return Err(BuildError::BadSoft {
1534 what: "a `soft` constraint over a real value is not supported (soft \
1535 constraints are relaxed over integral variables only)"
1536 .into(),
1537 });
1538 }
1539 let dists_from = self.dists.len();
1540 let built = if e.kind() == ExpressionKind::Dist {
1541 if real::dist_is_real(e) {
1542 self.real_dist(e)?;
1543 return Ok(Vec::new());
1544 }
1545 self.dist(e)?
1546 } else {
1547 if real::reads_real(e) {
1548 let cond = self.real_cond(e)?;
1549 self.real_conds.push(cond);
1550 return Ok(Vec::new());
1551 }
1552 to_bool(self.expr(e)?)
1553 };
1554 if soft {
1555 let dists = dists_from..self.dists.len();
1556 return Ok(vec![self.make_soft(built, dists)?]);
1557 }
1558 Ok(vec![built])
1559 }
1560 ConstraintKind::Implication | ConstraintKind::Conditional
1563 if real::constraint_reads_real(c) =>
1564 {
1565 let cond = self.real_constraint(c)?;
1566 self.real_conds.push(cond);
1567 Ok(Vec::new())
1568 }
1569 ConstraintKind::Implication => {
1570 let p = c.predicate().ok_or_else(|| BuildError::Missing {
1571 what: "predicate of an implication".into(),
1572 })?;
1573 let pred = to_bool(self.expr(p)?);
1574 let mut body = Vec::new();
1575 self.guard_depth += 1;
1576 for k in kids(c) {
1577 match self.constraint(k) {
1578 Ok(b) => body.extend(b),
1579 Err(e) => {
1580 self.guard_depth -= 1;
1581 return Err(e);
1582 }
1583 }
1584 }
1585 self.guard_depth -= 1;
1586 Ok(vec![Expr::Guard(
1589 Box::new(pred),
1590 Box::new(and_all(body)),
1591 Box::new(Expr::Const(Ty::BIT, BigUint::one())),
1592 Ty::BIT,
1593 )])
1594 }
1595 ConstraintKind::Conditional => {
1596 let p = c.predicate().ok_or_else(|| BuildError::Missing {
1597 what: "predicate of an if constraint".into(),
1598 })?;
1599 let pred = to_bool(self.expr(p)?);
1600 let arms = kids(c);
1601 if arms.is_empty() || arms.len() > 2 {
1602 return Err(BuildError::Missing {
1603 what: format!("one or two arms of an if constraint (got {})", arms.len()),
1604 });
1605 }
1606 self.guard_depth += 1;
1607 let arms_built = arms
1608 .iter()
1609 .map(|a| self.constraint(*a).map(and_all))
1610 .collect::<Result<Vec<_>, _>>();
1611 self.guard_depth -= 1;
1612 let mut arms_built = arms_built?.into_iter();
1613 let then_c = arms_built.next().ok_or_else(|| BuildError::Missing {
1614 what: "the then arm of an if constraint".into(),
1615 })?;
1616 let else_c = arms_built
1617 .next()
1618 .unwrap_or_else(|| Expr::Const(Ty::BIT, BigUint::one()));
1619 Ok(vec![Expr::Guard(
1620 Box::new(pred),
1621 Box::new(then_c),
1622 Box::new(else_c),
1623 Ty::BIT,
1624 )])
1625 }
1626 ConstraintKind::Uniqueness => self.unique(c),
1627 ConstraintKind::SolveBefore => {
1628 let split = self.solve_side_len(c)?;
1631 let mut ids = Vec::new();
1632 for k in c.children() {
1633 let e = k.as_expression().ok_or_else(|| BuildError::Missing {
1634 what: "expression operand of `solve ... before`".into(),
1635 })?;
1636 ids.push(self.solve_operand(e)?);
1637 }
1638 if split > ids.len() {
1639 return Err(BuildError::Missing {
1640 what: "operands of `solve ... before` matching its syntax".into(),
1641 });
1642 }
1643 let (first, later) = ids.split_at(split);
1644 for &a in first {
1645 for &b in later {
1646 match (a, b) {
1647 (SolveOperand::Int(a), SolveOperand::Int(b)) => {
1648 self.before.push((a, b));
1649 }
1650 (SolveOperand::Real(a), SolveOperand::Real(b)) => {
1651 self.real_before.push((a, b));
1652 }
1653 (SolveOperand::Size, SolveOperand::Int(_) | SolveOperand::Real(_)) => {}
1661 (SolveOperand::Int(_) | SolveOperand::Real(_), SolveOperand::Size)
1662 | (SolveOperand::Size, SolveOperand::Size) => {
1663 return Err(unsupported(
1664 "`solve ... before` placing a variable or another array size \
1665 before an array `.size()`",
1666 "array sizes are always solved first, as one joint sub-problem",
1667 ));
1668 }
1669 _ => {}
1673 }
1674 }
1675 }
1676 Ok(Vec::new())
1677 }
1678 ConstraintKind::Invalid => Err(BuildError::Missing {
1679 what: "well-formed constraint (the frontend diagnosed an error in it)".into(),
1680 }),
1681 ConstraintKind::DisableSoft => {
1682 self.disable_soft(c)?;
1684 Ok(Vec::new())
1685 }
1686 ConstraintKind::Foreach => self.foreach(c),
1687 other => Err(unsupported(format!("constraint kind {other:?}"), "C2")),
1688 }
1689 }
1690
1691 fn symbol_ref(&mut self, sym: Symbol<'_>, ty: Ty) -> Result<Expr, BuildError> {
1692 if self.frame.is_some()
1695 && sym.kind() == SymbolKind::ClassProperty
1696 && let Some(base) = self.fn_base.clone()
1697 {
1698 return self.based_state(&base, &[sym], ty);
1699 }
1700 let uid = sym.uid();
1701 if let Some(x) = self.pattern_binds.get(&uid) {
1702 return Ok(x.clone());
1703 }
1704 if let Some(&id) = self.var_ix.get(&uid) {
1705 if self.frame.is_some() {
1706 return Err(self.rand_read_in_function(sym.name()));
1707 }
1708 return Ok(Expr::Var(id, self.vars[id as usize].ty));
1709 }
1710 self.state_ref(sym, &[], 0, ty)
1711 }
1712
1713 fn state_ref(
1716 &mut self,
1717 root: Symbol<'_>,
1718 hops: &[Symbol<'_>],
1719 root_bit: u32,
1720 ty: Ty,
1721 ) -> Result<Expr, BuildError> {
1722 let key: Vec<SymbolId> = std::iter::once(root.uid())
1723 .chain(hops.iter().map(|h| h.uid()))
1724 .collect();
1725 let key = (key, root_bit);
1726 if let Some(&id) = self.state_ix.get(&key) {
1727 return Ok(Expr::State(id, self.states[id as usize].ty));
1728 }
1729 let mut name = root.name().to_string();
1730 let mut via = Vec::with_capacity(hops.len());
1731 for h in hops {
1732 name = format!("{name}.{}", h.name());
1733 let resolver =
1734 self.slot_of
1735 .as_ref()
1736 .ok_or_else(|| BuildError::ForeignObjectAccess {
1737 what: format!(
1738 "read of `{name}` (this builder was given no object layout to place \
1739 another object's members)"
1740 ),
1741 })?;
1742 let slot = resolver(*h).map_err(|why| BuildError::Missing {
1743 what: format!("field slot of `{name}`: {why}"),
1744 })?;
1745 via.push(StateHop {
1746 symbol: h.uid(),
1747 slot,
1748 });
1749 }
1750 let id = self.states.len() as u32;
1751 self.states.push(StateInfo {
1752 symbol: root.uid(),
1753 name,
1754 ty,
1755 via,
1756 window: None,
1757 root_bit,
1758 class_of: false,
1759 });
1760 self.state_ix.insert(key, id);
1761 Ok(Expr::State(id, ty))
1762 }
1763
1764 fn local_handle_root<'d>(mut e: Expression<'d>) -> Option<Expression<'d>> {
1767 loop {
1770 let kind = e.kind();
1771 if kind == ExpressionKind::MemberAccess {
1772 e = e.left()?;
1773 } else if kind == ExpressionKind::Conversion {
1774 e = e.conversion_operand()?;
1775 } else if kind == ExpressionKind::NamedValue
1776 || kind == ExpressionKind::HierarchicalValue
1777 {
1778 return e
1779 .referenced_symbol()
1780 .is_some_and(|s| s.name() != "this")
1781 .then_some(e);
1782 } else {
1783 return None;
1784 }
1785 }
1786 }
1787
1788 fn handle_path<'d>(e: Expression<'d>) -> Result<Vec<Symbol<'d>>, BuildError> {
1791 match e.kind() {
1792 ExpressionKind::NamedValue | ExpressionKind::HierarchicalValue => {
1793 let sym = e.referenced_symbol().ok_or_else(|| BuildError::Missing {
1794 what: "symbol of a named value".into(),
1795 })?;
1796 Ok(if sym.name() == "this" {
1797 Vec::new()
1798 } else {
1799 vec![sym]
1800 })
1801 }
1802 ExpressionKind::MemberAccess => {
1803 let mut path = Self::handle_path(Self::operand(e, "a member access")?)?;
1804 path.push(e.member_symbol().ok_or_else(|| BuildError::Missing {
1805 what: "member symbol of a member access".into(),
1806 })?);
1807 Ok(path)
1808 }
1809 ExpressionKind::Conversion => {
1810 let inner = e.conversion_operand().ok_or_else(|| BuildError::Missing {
1811 what: "operand of a conversion".into(),
1812 })?;
1813 Self::handle_path(inner)
1814 }
1815 ExpressionKind::ElementSelect => {
1818 let arr = e.select_value().ok_or_else(|| BuildError::Missing {
1819 what: "array of an element select".into(),
1820 })?;
1821 match arr.kind() {
1822 ExpressionKind::NamedValue | ExpressionKind::HierarchicalValue => {
1823 Self::handle_path(arr)
1824 }
1825 other => Err(unsupported(
1826 format!(
1827 "an element of a `{other:?}` array of handles as the object of a member access"
1828 ),
1829 "C17 follow-up",
1830 )),
1831 }
1832 }
1833 other => Err(unsupported(
1834 format!("a `{other:?}` expression as the object of a member access"),
1835 "C17 follow-up",
1836 )),
1837 }
1838 }
1839
1840 fn handle_root_element(&mut self, e: Expression<'_>) -> Result<Option<(u32, u32)>, BuildError> {
1846 let mut cur = e;
1847 loop {
1850 let kind = cur.kind();
1851 if kind == ExpressionKind::MemberAccess {
1852 cur = Self::operand(cur, "a member access")?;
1853 } else if kind == ExpressionKind::Conversion {
1854 cur = cur
1855 .conversion_operand()
1856 .ok_or_else(|| BuildError::Missing {
1857 what: "operand of a conversion".into(),
1858 })?;
1859 } else if kind == ExpressionKind::ElementSelect {
1860 break;
1861 } else {
1862 return Ok(None);
1863 }
1864 }
1865 let arr = cur.select_value().ok_or_else(|| BuildError::Missing {
1866 what: "array of an element select".into(),
1867 })?;
1868 let at = arr
1869 .expr_type()
1870 .map(|t| t.canonical())
1871 .ok_or_else(|| BuildError::Missing {
1872 what: "type of an array of handles".into(),
1873 })?;
1874 if !at.is_unpacked_array() || at.fixed_unpacked_array_range().width() == 0 {
1875 return Err(unsupported(
1876 "an element of a dynamic array, queue or associative array of handles in a constraint",
1877 "C17 follow-up",
1878 ));
1879 }
1880 let sel = cur.selector().ok_or_else(|| BuildError::Missing {
1881 what: "index of an element select".into(),
1882 })?;
1883 let ix = self.expr(sel)?;
1884 let i = Self::constant_index(&ix).ok_or_else(|| {
1885 unsupported(
1886 "an element of an array of handles at a computed index",
1887 "C17 follow-up",
1888 )
1889 })?;
1890 let r = at.fixed_unpacked_array_range();
1891 let lo = i64::from(r.left).min(i64::from(r.right));
1892 let n = i64::from(r.width());
1893 if !(0..n).contains(&(i - lo)) {
1894 return Err(BuildError::OutOfRangeSelect {
1895 what: format!("handle array index {i} outside [{}:{}]", r.left, r.right),
1896 });
1897 }
1898 Ok(Some((((i - lo) * 64) as u32, (n * 64) as u32)))
1899 }
1900
1901 fn handle_state(&mut self, e: Expression<'_>, ty: Ty) -> Result<Expr, BuildError> {
1905 let path = Self::handle_path(e)?;
1906 if path.is_empty() {
1907 return Err(unsupported("a bare `this` as a value", "C17 follow-up"));
1908 }
1909 if self.frame.is_some()
1910 && let Some(base) = self.fn_base.clone()
1911 {
1912 return self.based_state(&base, &path, ty);
1915 }
1916 let Some((root, hops)) = path.split_first() else {
1917 return Err(unsupported("a bare `this` as a value", "C17 follow-up"));
1918 };
1919 if self.var_ix.contains_key(&root.uid()) {
1920 return Err(unsupported(
1921 format!("the randomized handle `{}` in a constraint", root.name()),
1922 "C17 follow-up",
1923 ));
1924 }
1925 let elem = self.handle_root_element(e)?;
1928 for (i, step) in path[..path.len() - 1].iter().enumerate() {
1929 let is_handle = (i == 0 && elem.is_some())
1930 || step.value_type().is_some_and(|t| t.canonical().is_class());
1931 if !is_handle {
1932 return Err(unsupported(
1933 format!(
1934 "a member access through `{}` (step {i}), which is not a class handle",
1935 step.name()
1936 ),
1937 "C17 follow-up",
1938 ));
1939 }
1940 }
1941 let bit = elem.map_or(0, |(b, _)| b);
1942 let out = self.state_ref(*root, hops, bit, ty)?;
1943 if let (Some((bit, whole)), true, Expr::State(id, _)) = (elem, hops.is_empty(), &out) {
1944 self.states[*id as usize].window = Some(Window {
1946 bit,
1947 width: 64,
1948 field_width: whole,
1949 });
1950 }
1951 Ok(out)
1952 }
1953
1954 fn handle_value(&mut self, e: Expression<'_>) -> Result<Expr, BuildError> {
1957 match e.kind() {
1958 ExpressionKind::NullLiteral => Ok(Expr::Const(HANDLE_TY, BigUint::zero())),
1959 ExpressionKind::Conversion => {
1962 let inner = e.conversion_operand().ok_or_else(|| BuildError::Missing {
1963 what: "operand of a conversion".into(),
1964 })?;
1965 self.handle_value(inner)
1966 }
1967 ExpressionKind::NamedValue
1968 | ExpressionKind::HierarchicalValue
1969 | ExpressionKind::ElementSelect
1970 | ExpressionKind::MemberAccess => self.handle_state(e, HANDLE_TY),
1971 other => Err(unsupported(
1972 format!("a `{other:?}` class-handle expression"),
1973 "C17 follow-up",
1974 )),
1975 }
1976 }
1977
1978 pub fn build_expr(&mut self, e: Expression<'_>) -> Result<Expr, BuildError> {
1981 let out = self.expr(e)?;
1982 out.validate()?;
1983 Ok(out)
1984 }
1985
1986 fn operand<'d>(e: Expression<'d>, what: &str) -> Result<Expression<'d>, BuildError> {
1987 e.left().ok_or_else(|| BuildError::Missing {
1988 what: format!("operand of {what}"),
1989 })
1990 }
1991
1992 fn expr(&mut self, e: Expression<'_>) -> Result<Expr, BuildError> {
1993 let kind = e.kind();
1994 if is_handle_type(e.expr_type()) {
1995 return self.handle_value(e);
1996 }
1997 let ty = ty_of(e.expr_type(), &format!("a `{kind:?}` expression"))?;
1998 let pattern_cond = kind == ExpressionKind::ConditionalOp
2002 && e.conditions().iter().any(|c| c.pattern().is_some());
2003 if !pattern_cond
2004 && let Some(cv) = e.constant_value()
2005 && let Some(i) = cv.as_integer()
2006 {
2007 return Ok(Expr::Const(ty, svint_to_biguint(i, ty)?));
2008 }
2009 match kind {
2010 ExpressionKind::NamedValue | ExpressionKind::HierarchicalValue => {
2011 let sym = e.referenced_symbol().ok_or_else(|| BuildError::Missing {
2012 what: "symbol of a named value".into(),
2013 })?;
2014 if let Some(local) = self.frame_var(sym) {
2015 return Ok(local);
2016 }
2017 if sym.kind() == SymbolKind::Iterator {
2018 self.not_in_function("an iterator variable")?;
2019 return self.loop_ref(sym, ty);
2020 }
2021 if self.is_call_local(e) {
2022 self.not_in_function("a local of an inline constraint's call site")?;
2023 return Ok(self.capture_ref(sym, ty));
2024 }
2025 self.symbol_ref(sym, ty)
2026 }
2027 ExpressionKind::MemberAccess => {
2028 if let Some(chain) = aggregate::aggregate_chain(e, &self.forced_roots)
2031 && !chain.steps.is_empty()
2032 {
2033 return self.aggregate_value(chain, ty);
2034 }
2035 let base = Self::operand(e, "a member access")?;
2036 let is_this = base.kind() == ExpressionKind::NamedValue
2037 && base.referenced_symbol().is_some_and(|s| s.name() == "this");
2038 let member = e.member_symbol().ok_or_else(|| BuildError::Missing {
2039 what: "member symbol of a member access".into(),
2040 })?;
2041 if !is_this {
2042 if is_handle_type(base.expr_type()) {
2043 if let Some(root) = Self::local_handle_root(base)
2046 && self.is_call_local(root)
2047 {
2048 self.not_in_function("a local of an inline constraint's call site")?;
2049 let path = Self::handle_path(e)?;
2050 let (r, hops) =
2051 path.split_first().ok_or_else(|| BuildError::Missing {
2052 what: "handle path of a member read".into(),
2053 })?;
2054 return Ok(self.capture_path(*r, hops, ty));
2055 }
2056 return self.handle_state(e, ty);
2058 }
2059 let base_ty = base.expr_type();
2062 if !base_ty.is_some_and(|t| t.is_integral() && (t.is_struct() || t.is_union()))
2063 {
2064 let what = format!("member access `.{}`", member.name());
2065 return Err(unsupported(
2066 format!(
2067 "{what} on a value that is neither a class handle, a packed \
2068 struct or union, nor a member of an unpacked struct property \
2069 of this class"
2070 ),
2071 "C14",
2072 ));
2073 }
2074 let whole = self.expr(base)?;
2075 let low = u32::try_from(member.field_bit_offset()).map_err(|_| {
2076 BuildError::Missing {
2077 what: format!("bit offset of packed member `{}`", member.name()),
2078 }
2079 })?;
2080 let slice = Expr::Slice(Box::new(whole), low, Ty::unsigned(ty.width));
2081 return Ok(if slice.ty() == ty {
2082 slice
2083 } else {
2084 Expr::Resize(Box::new(slice), ty)
2085 });
2086 }
2087 self.symbol_ref(member, ty)
2088 }
2089 ExpressionKind::UnaryOp => {
2090 let op = e.unary_op().ok_or_else(|| BuildError::Missing {
2091 what: "operator of a unary expression".into(),
2092 })?;
2093 let a = self.expr(Self::operand(e, "a unary operator")?)?;
2094 let (uop, rty) = match op {
2095 UnaryOp::Plus => return Ok(a),
2096 UnaryOp::Minus => (UnOp::Neg, ty),
2097 UnaryOp::BitwiseNot => (UnOp::BitNot, ty),
2098 UnaryOp::BitwiseAnd => (UnOp::RedAnd, Ty::BIT),
2099 UnaryOp::BitwiseOr => (UnOp::RedOr, Ty::BIT),
2100 UnaryOp::BitwiseXor => (UnOp::RedXor, Ty::BIT),
2101 UnaryOp::BitwiseNand => (UnOp::RedNand, Ty::BIT),
2102 UnaryOp::BitwiseNor => (UnOp::RedNor, Ty::BIT),
2103 UnaryOp::BitwiseXnor => (UnOp::RedXnor, Ty::BIT),
2104 UnaryOp::LogicalNot => (UnOp::LogNot, Ty::BIT),
2105 other => {
2106 return Err(unsupported(
2107 format!("the side-effecting operator {other:?}"),
2108 "C2",
2109 ));
2110 }
2111 };
2112 Ok(Expr::Unary(uop, Box::new(a), rty))
2113 }
2114 ExpressionKind::BinaryOp => self.binary(e, ty),
2115 ExpressionKind::ConditionalOp => {
2116 let mut bound = Vec::new();
2117 let r = self.conditional(e, ty, &mut bound);
2118 for uid in bound {
2119 self.pattern_binds.remove(&uid);
2120 }
2121 r
2122 }
2123 ExpressionKind::Conversion => self.conversion(e, ty),
2124 ExpressionKind::Concatenation => {
2125 let mut parts = Vec::new();
2126 let mut w = 0u32;
2127 for o in e.operands() {
2128 let p = self.expr(o)?;
2129 w += p.ty().width;
2130 parts.push(p);
2131 }
2132 Ok(Expr::Concat(parts, Ty::unsigned(w)))
2133 }
2134 ExpressionKind::Replication => {
2135 let n = e
2136 .replication_count()
2137 .and_then(|c| c.constant_value())
2138 .and_then(|c| c.as_i64())
2139 .ok_or_else(|| BuildError::Missing {
2140 what: "constant count of a replication".into(),
2141 })?;
2142 if n < 1 {
2143 return Err(unsupported("a replication with a count below 1", "C2"));
2144 }
2145 let inner =
2146 self.expr(e.replication_concat().ok_or_else(|| BuildError::Missing {
2147 what: "operand of a replication".into(),
2148 })?)?;
2149 let w = inner.ty().width * n as u32;
2150 Ok(Expr::Concat(vec![inner; n as usize], Ty::unsigned(w)))
2151 }
2152 ExpressionKind::Inside => self.inside(e),
2153 ExpressionKind::ElementSelect => self.element_select(e, ty),
2154 ExpressionKind::RangeSelect => self.range_select(e, ty),
2155 ExpressionKind::Call => {
2156 if e.is_system_call() == Some(true)
2157 && self.frame.is_none()
2158 && let Some(r) = self.array_call(e, ty)
2159 {
2160 return r;
2161 }
2162 let name = e.call_subroutine_name().unwrap_or_default();
2163 if e.is_system_call() == Some(true) && (name == "$signed" || name == "$unsigned") {
2164 let args = e.operands();
2165 let [arg] = args.as_slice() else {
2166 return Err(BuildError::Missing {
2167 what: format!("single argument of {name}"),
2168 });
2169 };
2170 let a = self.expr(*arg)?;
2171 return Ok(Expr::Resize(Box::new(a), ty));
2172 }
2173 if e.is_system_call() == Some(true)
2174 && let Some(r) = self.bit_count_call(e, ty)
2175 {
2176 return r;
2177 }
2178 if e.is_system_call() == Some(true) {
2179 return Err(unsupported(format!("a call to `{name}`"), "C13"));
2180 }
2181 self.user_call(e, ty)
2182 }
2183 ExpressionKind::LValueReference => self.lvalue_reference(),
2184 ExpressionKind::Dist => Err(BuildError::BadDist {
2185 what: "a `dist` nested inside another expression (it is a constraint item, \
2186 never an operand)"
2187 .into(),
2188 }),
2189 ExpressionKind::UnboundedLiteral => Err(unsupported(
2190 "an unbounded `$` outside an `inside` range",
2191 "C2",
2192 )),
2193 other => Err(unsupported(format!("a `{other:?}` expression"), "C2")),
2194 }
2195 }
2196
2197 fn conditional(
2203 &mut self,
2204 e: Expression<'_>,
2205 ty: Ty,
2206 bound: &mut Vec<SymbolId>,
2207 ) -> Result<Expr, BuildError> {
2208 let mut tests = Vec::new();
2209 for c in e.conditions() {
2210 let subject = self.expr(c.expr())?;
2211 tests.push(match c.pattern() {
2212 None => to_bool(subject),
2213 Some(p) => self.pattern_test(&p, subject, bound)?,
2214 });
2215 }
2216 let t = self.expr(e.true_value().ok_or_else(|| BuildError::Missing {
2217 what: "true arm of a conditional".into(),
2218 })?)?;
2219 for uid in bound.drain(..) {
2221 self.pattern_binds.remove(&uid);
2222 }
2223 let f = self.expr(e.false_value().ok_or_else(|| BuildError::Missing {
2224 what: "false arm of a conditional".into(),
2225 })?)?;
2226 Ok(Expr::Ite(
2227 Box::new(and_all(tests)),
2228 Box::new(t),
2229 Box::new(f),
2230 ty,
2231 ))
2232 }
2233
2234 fn pattern_test(
2241 &mut self,
2242 pat: &sv_lang::Pattern<'_>,
2243 subject: Expr,
2244 bound: &mut Vec<SymbolId>,
2245 ) -> Result<Expr, BuildError> {
2246 match pat.kind() {
2247 PatternKind::Constant => {
2248 let v = self.expr(pat.value_expr().ok_or_else(|| BuildError::Missing {
2249 what: "value of a constant pattern".into(),
2250 })?)?;
2251 let (a, b) = (subject.ty(), v.ty());
2252 let ct = Ty {
2253 width: a.width.max(b.width),
2254 signed: a.signed && b.signed,
2255 };
2256 Ok(bin(BinOp::Eq, coerce(subject, ct), coerce(v, ct), Ty::BIT))
2257 }
2258 PatternKind::Wildcard => Ok(Expr::Const(Ty::BIT, BigUint::one())),
2259 PatternKind::Variable => {
2260 if let Some(var) = pat.variable()
2261 && !var.name().is_empty()
2262 {
2263 let uid = var.uid();
2264 self.pattern_binds.insert(uid.clone(), subject);
2265 bound.push(uid);
2266 }
2267 Ok(Expr::Const(Ty::BIT, BigUint::one()))
2268 }
2269 other => Err(unsupported(
2270 format!(
2271 "a {other:?} pattern in a constraint (it matches a tagged union or an \
2272 unpacked struct, which is never a random variable)"
2273 ),
2274 "C2",
2275 )),
2276 }
2277 }
2278
2279 fn binary(&mut self, e: Expression<'_>, ty: Ty) -> Result<Expr, BuildError> {
2280 let op = e.binary_op().ok_or_else(|| BuildError::Missing {
2281 what: "operator of a binary expression".into(),
2282 })?;
2283 let (lhs, rhs) = match (e.left(), e.right()) {
2284 (Some(l), Some(r)) => (l, r),
2285 _ => {
2286 return Err(BuildError::Missing {
2287 what: "operands of a binary expression".into(),
2288 });
2289 }
2290 };
2291 let (bop, rty) = match op {
2292 BinaryOp::CaseEquality => return Err(BuildError::FourStateOperator { op: "===" }),
2293 BinaryOp::CaseInequality => return Err(BuildError::FourStateOperator { op: "!==" }),
2294 BinaryOp::WildcardEquality => return Err(BuildError::FourStateOperator { op: "==?" }),
2295 BinaryOp::WildcardInequality => {
2296 return Err(BuildError::FourStateOperator { op: "!=?" });
2297 }
2298 BinaryOp::Power => {
2299 let l = self.expr(lhs)?;
2300 let r = self.expr(rhs)?;
2301 return Ok(power(l, r, ty));
2302 }
2303 BinaryOp::Add => (BinOp::Add, ty),
2304 BinaryOp::Subtract => (BinOp::Sub, ty),
2305 BinaryOp::Multiply => (BinOp::Mul, ty),
2306 BinaryOp::Divide => (BinOp::Div, ty),
2307 BinaryOp::Mod => (BinOp::Mod, ty),
2308 BinaryOp::BinaryAnd => (BinOp::And, ty),
2309 BinaryOp::BinaryOr => (BinOp::Or, ty),
2310 BinaryOp::BinaryXor => (BinOp::Xor, ty),
2311 BinaryOp::BinaryXnor => (BinOp::Xnor, ty),
2312 BinaryOp::Equality => (BinOp::Eq, Ty::BIT),
2313 BinaryOp::Inequality => (BinOp::Ne, Ty::BIT),
2314 BinaryOp::GreaterThanEqual => (BinOp::Ge, Ty::BIT),
2315 BinaryOp::GreaterThan => (BinOp::Gt, Ty::BIT),
2316 BinaryOp::LessThanEqual => (BinOp::Le, Ty::BIT),
2317 BinaryOp::LessThan => (BinOp::Lt, Ty::BIT),
2318 BinaryOp::LogicalAnd => (BinOp::LogAnd, Ty::BIT),
2319 BinaryOp::LogicalOr => (BinOp::LogOr, Ty::BIT),
2320 BinaryOp::LogicalImplication => (BinOp::LogImpl, Ty::BIT),
2321 BinaryOp::LogicalEquivalence => (BinOp::LogEquiv, Ty::BIT),
2322 BinaryOp::LogicalShiftLeft | BinaryOp::ArithmeticShiftLeft => (BinOp::Shl, ty),
2323 BinaryOp::LogicalShiftRight => (BinOp::Shr, ty),
2324 BinaryOp::ArithmeticShiftRight => (BinOp::Ashr, ty),
2325 };
2326 let whole = |t: Option<Type<'_>>| {
2332 t.is_some_and(|t| {
2333 let c = t.canonical();
2334 c.is_unpacked_array() || c.is_unpacked_struct()
2335 })
2336 };
2337 if matches!(bop, BinOp::Eq | BinOp::Ne) && whole(lhs.expr_type()) && whole(rhs.expr_type())
2338 {
2339 let chains = aggregate::aggregate_chain(lhs, &self.forced_roots)
2340 .zip(aggregate::aggregate_chain(rhs, &self.forced_roots));
2341 let Some((lc, rc)) = chains else {
2342 return Err(unsupported(
2343 "an array comparison whose operand is not a property (or a member of a \
2344 struct property) of this class",
2345 "C14",
2346 ));
2347 };
2348 let l = self.aggregate_bitstream(lc)?;
2349 let r = self.aggregate_bitstream(rc)?;
2350 return Ok(bin(bop, l, r, rty));
2351 }
2352 let is_string =
2356 |e: Expression<'_>| e.expr_type().is_some_and(|t| t.canonical().is_string());
2357 if is_string(lhs) || is_string(rhs) {
2358 if !matches!(bop, BinOp::Eq | BinOp::Ne) {
2359 return Err(unsupported(
2360 format!("the string operator {op:?} (only `==` and `!=` compare strings)"),
2361 "C13",
2362 ));
2363 }
2364 let l = self.string_id(lhs)?;
2365 let r = self.string_id(rhs)?;
2366 return Ok(bin(bop, l, r, rty));
2367 }
2368 let l = self.expr(lhs)?;
2369 let r = self.expr(rhs)?;
2370 Ok(bin(bop, l, r, rty))
2371 }
2372
2373 fn string_id(&mut self, e: Expression<'_>) -> Result<Expr, BuildError> {
2377 if let Some(cv) = e.constant_value()
2378 && let Some(s) = cv.as_str()
2379 {
2380 let id = crate::strings::intern(s.as_bytes());
2381 return Ok(Expr::Const(HANDLE_TY, BigUint::from(id)));
2382 }
2383 match e.kind() {
2384 ExpressionKind::Conversion => {
2385 let inner = e.conversion_operand().ok_or_else(|| BuildError::Missing {
2386 what: "operand of a conversion".into(),
2387 })?;
2388 self.string_id(inner)
2389 }
2390 ExpressionKind::NamedValue
2391 | ExpressionKind::HierarchicalValue
2392 | ExpressionKind::MemberAccess => {
2393 let sym = e.referenced_symbol().or_else(|| e.member_symbol());
2394 if sym.is_some_and(|s| self.var_ix.contains_key(&s.uid())) {
2395 return Err(unsupported("a `rand` string", "C13"));
2396 }
2397 self.handle_state(e, HANDLE_TY)
2398 }
2399 other => Err(unsupported(
2400 format!("a `{other:?}` string operand of a comparison"),
2401 "C13",
2402 )),
2403 }
2404 }
2405
2406 fn conversion(&mut self, e: Expression<'_>, to: Ty) -> Result<Expr, BuildError> {
2407 let operand = e.conversion_operand().ok_or_else(|| BuildError::Missing {
2408 what: "operand of a conversion".into(),
2409 })?;
2410 let kind = e.conversion_kind().ok_or_else(|| BuildError::Missing {
2411 what: "kind of a conversion".into(),
2412 })?;
2413 if operand.expr_type().is_some_and(|t| !t.is_integral())
2417 && let Some(chain) = aggregate::aggregate_chain(operand, &self.forced_roots)
2418 {
2419 let x = self.aggregate_bitstream(chain)?;
2420 return Ok(if x.ty() == to {
2421 x
2422 } else {
2423 Expr::Resize(Box::new(x), to)
2424 });
2425 }
2426 let x = self.expr(operand)?;
2427 match kind {
2428 1 => Ok(coerce(x, to)),
2430 0 | 3 => Ok(if x.ty() == to {
2432 x
2433 } else {
2434 Expr::Resize(Box::new(x), to)
2435 }),
2436 other => Err(unsupported(
2437 format!("a conversion of kind {other} (streaming or bit-stream cast)"),
2438 "C2",
2439 )),
2440 }
2441 }
2442
2443 fn range_op(&self, item: Expression<'_>) -> Result<RangeOp, BuildError> {
2446 for tree in self.trees {
2447 if let Some(node) = item.syntax(tree) {
2448 let r =
2449 ValueRangeExpressionSyntax::cast(node).ok_or_else(|| BuildError::Missing {
2450 what: "value-range syntax for a ValueRange expression".into(),
2451 })?;
2452 let op = r.op().ok_or_else(|| BuildError::Missing {
2453 what: "operator of a value range".into(),
2454 })?;
2455 return match op.text() {
2456 ":" => Ok(RangeOp::Span),
2457 "+/-" => Ok(RangeOp::Abs),
2458 "+%-" => Ok(RangeOp::Pct),
2459 other => Err(unsupported(
2460 format!("the value-range operator `{other}`"),
2461 "C7",
2462 )),
2463 };
2464 }
2465 }
2466 Err(BuildError::Missing {
2467 what: "syntax node for a value range (is its syntax tree passed to the builder?)"
2468 .into(),
2469 })
2470 }
2471
2472 fn value_range(&mut self, item: Expression<'_>, subject: Ty) -> Result<Bounds, BuildError> {
2476 let (l, r) = match (item.left(), item.right()) {
2477 (Some(l), Some(r)) => (l, r),
2478 _ => {
2479 return Err(BuildError::Missing {
2480 what: "bounds of a value range".into(),
2481 });
2482 }
2483 };
2484 let op = self.range_op(item)?;
2485 let is_unbounded = |b: Expression<'_>| {
2487 let mut peeled = b;
2488 while peeled.kind() == ExpressionKind::Conversion {
2489 match peeled.conversion_operand() {
2490 Some(inner) => peeled = inner,
2491 None => break,
2492 }
2493 }
2494 peeled.kind() == ExpressionKind::UnboundedLiteral
2495 };
2496 if op == RangeOp::Span {
2497 let mut side = |b: Expression<'_>| -> Result<Option<(Ty, Expr)>, BuildError> {
2498 if is_unbounded(b) {
2499 return Ok(None);
2500 }
2501 let bx = self.expr(b)?;
2502 let t = common_ty(subject, bx.ty());
2503 Ok(Some((t, coerce(bx, t))))
2504 };
2505 let lo = side(l)?;
2506 let hi = side(r)?;
2507 return Ok(Bounds { lo, hi });
2508 }
2509 if is_unbounded(l) || is_unbounded(r) {
2510 return Err(BuildError::BadDist {
2511 what: "`$` as an operand of a `+/-` or `+%-` range".into(),
2512 });
2513 }
2514 let a = self.expr(l)?;
2515 let b = self.expr(r)?;
2516 let wide = Ty::signed(2 * subject.width.max(a.ty().width).max(b.ty().width) + 8);
2517 let (a, b) = (coerce(a, wide), coerce(b, wide));
2518 let delta = match op {
2519 RangeOp::Abs => b,
2520 _ => {
2521 let hundred = iconst(wide, 100);
2522 bin(
2523 BinOp::Div,
2524 bin(BinOp::Mul, a.clone(), b, wide),
2525 hundred,
2526 wide,
2527 )
2528 }
2529 };
2530 let lo = bin(BinOp::Sub, a.clone(), delta.clone(), wide);
2531 let hi = bin(BinOp::Add, a, delta, wide);
2532 Ok(Bounds {
2533 lo: Some((wide, lo)),
2534 hi: Some((wide, hi)),
2535 })
2536 }
2537
2538 fn dist(&mut self, e: Expression<'_>) -> Result<Expr, BuildError> {
2542 let runtime = self.in_runtime_loop();
2546 let saved = if runtime {
2547 self.no_arrays
2548 } else {
2549 self.no_arrays.replace("a `dist` constraint")
2550 };
2551 let r = self.dist_item(e, runtime);
2552 self.no_arrays = saved;
2553 r
2554 }
2555
2556 fn dist_item(&mut self, e: Expression<'_>, runtime: bool) -> Result<Expr, BuildError> {
2557 let bad = |what: &str| BuildError::BadDist { what: what.into() };
2558 let left = e.dist_left().ok_or_else(|| BuildError::Missing {
2559 what: "distributed expression of a `dist`".into(),
2560 })?;
2561 let mut left = left;
2566 while left.kind() == ExpressionKind::Conversion {
2567 match left.conversion_operand() {
2568 Some(inner) => left = inner,
2569 None => break,
2570 }
2571 }
2572 let subject = self.expr(left)?;
2573 let mut vars: Vec<VarId> = Vec::new();
2574 let mut randc: Option<VarId> = None;
2575 subject.for_each_var(&mut |v, _| {
2576 vars.push(v);
2577 if self.vars[v as usize].randc {
2578 randc = Some(v);
2579 }
2580 });
2581 vars.sort_unstable();
2582 vars.dedup();
2583 if let Some(v) = randc {
2584 return Err(BuildError::BadDist {
2585 what: format!(
2586 "`randc` variable `{}` in a `dist` (randc variables take no weights)",
2587 self.vars[v as usize].name
2588 ),
2589 });
2590 }
2591 let plain_var = match &subject {
2592 Expr::Var(v, _) => Some(*v),
2593 _ => None,
2594 };
2595 let sty = subject.ty();
2596 let state_only = |x: &Expr, what: &str| -> Result<(), BuildError> {
2597 let mut has_var = false;
2598 x.for_each_var(&mut |_, _| has_var = true);
2599 if has_var {
2600 return Err(BuildError::BadDist {
2601 what: format!(
2602 "a random variable in {what}; weights and bounds are fixed values"
2603 ),
2604 });
2605 }
2606 Ok(())
2607 };
2608 let weight_of = |this: &mut Self,
2609 w: Option<sv_lang::DistWeight<'_>>|
2610 -> Result<(Expr, bool), BuildError> {
2611 match w {
2612 None => Ok((Expr::constant(Ty::unsigned(32), 1u32), false)),
2614 Some(w) => {
2615 let x = this.expr(w.expr)?;
2616 state_only(&x, "a `dist` weight")?;
2617 Ok((x, w.kind == DistWeightKind::PerRange))
2618 }
2619 }
2620 };
2621 let mut items = Vec::new();
2622 for it in e.dist_items() {
2623 let v = it.value();
2624 let (member, ty, span) = if v.kind() == ExpressionKind::ValueRange {
2625 let b = self.value_range(v, sty)?;
2626 let ty = match (&b.lo, &b.hi) {
2629 (Some((t, _)), _) | (_, Some((t, _))) => *t,
2630 (None, None) => sty,
2631 };
2632 let mut conj = Vec::new();
2633 for (bound, cmp) in [(&b.lo, BinOp::Ge), (&b.hi, BinOp::Le)] {
2634 if let Some((t, x)) = bound {
2635 state_only(x, "a `dist` range bound")?;
2636 conj.push(bin(cmp, coerce(subject.clone(), *t), x.clone(), Ty::BIT));
2637 }
2638 }
2639 (
2640 and_all(conj),
2641 ty,
2642 Span::Range {
2643 lo: b.lo.map(|(_, x)| x),
2644 hi: b.hi.map(|(_, x)| x),
2645 },
2646 )
2647 } else {
2648 let x = self.expr(v)?;
2649 state_only(&x, "a `dist` value")?;
2650 let t = common_ty(sty, x.ty());
2651 let x = coerce(x, t);
2652 let m = bin(BinOp::Eq, coerce(subject.clone(), t), x.clone(), Ty::BIT);
2653 (m, t, Span::Point(x))
2654 };
2655 let (w, per_range) = weight_of(self, it.weight())?;
2656 items.push(Item {
2657 member,
2658 ty,
2659 span,
2660 weight: w,
2661 per_range,
2662 });
2663 }
2664 let default = match e.dist_default_weight() {
2665 Some(w) => {
2666 let (weight, per_range) = weight_of(self, Some(w))?;
2667 Some(DefaultWeight { weight, per_range })
2668 }
2669 None => None,
2670 };
2671 if items.is_empty() && default.is_none() {
2672 return Err(bad("a `dist` with no items"));
2673 }
2674 let d = Dist {
2675 subject_ty: sty,
2676 plain_var,
2677 vars,
2678 items,
2679 default,
2680 subject: runtime.then_some(subject),
2681 };
2682 let sel = SELECTOR_BASE + self.dists.len() as u32;
2683 let conjunct = d.conjunct(sel);
2684 self.dists.push(d);
2685 Ok(conjunct)
2686 }
2687
2688 fn inside(&mut self, e: Expression<'_>) -> Result<Expr, BuildError> {
2689 let left = self.expr(e.inside_left().ok_or_else(|| BuildError::Missing {
2690 what: "left operand of `inside`".into(),
2691 })?)?;
2692 let items = e.inside_range_list();
2693 if items.is_empty() {
2694 return Err(BuildError::Missing {
2695 what: "candidates of `inside`".into(),
2696 });
2697 }
2698 let mut acc: Option<Expr> = None;
2703 for item in items {
2704 let term = if item.kind() == ExpressionKind::ValueRange {
2705 let b = self.value_range(item, left.ty())?;
2708 let mut conj: Vec<Expr> = Vec::new();
2709 for (bound, cmp) in [(b.lo, BinOp::Ge), (b.hi, BinOp::Le)] {
2710 if let Some((t, x)) = bound {
2711 conj.push(bin(cmp, coerce(left.clone(), t), x, Ty::BIT));
2712 }
2713 }
2714 and_all(conj)
2715 } else if item
2716 .expr_type()
2717 .is_some_and(|t| arrays::array_shape(t).is_some())
2718 {
2719 self.inside_array(&left, item)?
2720 } else {
2721 let ix = self.expr(item)?;
2722 let t = common_ty(left.ty(), ix.ty());
2723 bin(BinOp::Eq, coerce(left.clone(), t), coerce(ix, t), Ty::BIT)
2724 };
2725 acc = Some(match acc {
2726 None => term,
2727 Some(prev) => bin(BinOp::LogOr, prev, term, Ty::BIT),
2728 });
2729 }
2730 Ok(acc.expect("candidates non-empty, checked above"))
2731 }
2732
2733 fn select_base(&mut self, e: Expression<'_>) -> Result<(Expr, Geom), BuildError> {
2735 let base = e.select_value().ok_or_else(|| BuildError::Missing {
2736 what: "base of a select".into(),
2737 })?;
2738 let bt = base.expr_type().ok_or_else(|| BuildError::Missing {
2739 what: "type of a select base".into(),
2740 })?;
2741 let bx = self.expr(base)?;
2742 let geom = Geom::of(bt, bx.ty().width, "a select base")?;
2743 Ok((bx, geom))
2744 }
2745
2746 fn wide_index(&mut self, idx: Expression<'_>) -> Result<(Expr, Ty), BuildError> {
2749 let ix = self.expr(idx)?;
2750 let wide = Ty::signed(ix.ty().width.max(32) + 2);
2751 Ok((Expr::Resize(Box::new(ix), wide), wide))
2752 }
2753
2754 fn dyn_window(base: Expr, pos: Expr, wide: Ty, ew: u32, out_width: u32, ty: Ty) -> Expr {
2756 let off = if ew > 1 {
2757 bin(BinOp::Mul, pos, iconst(wide, i64::from(ew)), wide)
2758 } else {
2759 pos
2760 };
2761 let bty = base.ty();
2762 let shifted = bin(BinOp::Shr, base, off, bty);
2763 Expr::Resize(
2764 Box::new(Expr::Resize(Box::new(shifted), Ty::unsigned(out_width))),
2765 ty,
2766 )
2767 }
2768
2769 fn static_window(
2770 base: Expr,
2771 geom: &Geom,
2772 lo_idx: i64,
2773 hi_idx: i64,
2774 what: &str,
2775 ty: Ty,
2776 ) -> Result<Expr, BuildError> {
2777 let (pl, ph) = (geom.pos(lo_idx), geom.pos(hi_idx));
2778 let (low, high) = (pl.min(ph), pl.max(ph));
2779 if low < 0 || high >= geom.elems() {
2780 return Err(BuildError::OutOfRangeSelect {
2781 what: format!(
2782 "{what}[{lo_idx}..{hi_idx}] outside [{}:{}]",
2783 geom.left, geom.right
2784 ),
2785 });
2786 }
2787 let width = (high - low + 1) as u32 * geom.ew;
2788 let slice = Expr::Slice(Box::new(base), low as u32 * geom.ew, Ty::unsigned(width));
2789 Ok(if slice.ty() == ty {
2790 slice
2791 } else {
2792 Expr::Resize(Box::new(slice), ty)
2793 })
2794 }
2795
2796 fn element_select(&mut self, e: Expression<'_>, ty: Ty) -> Result<Expr, BuildError> {
2797 if let Some(chain) = aggregate::aggregate_chain(e, &self.forced_roots)
2799 && !chain.steps.is_empty()
2800 {
2801 return self.aggregate_value(chain, ty);
2802 }
2803 if let Some(arr) = e.select_value()
2804 && arr
2805 .expr_type()
2806 .is_some_and(|t| arrays::array_shape(t).is_some())
2807 {
2808 self.not_in_function("an array element")?;
2809 return self.array_element(e, arr, ty);
2810 }
2811 let (base, geom) = self.select_base(e)?;
2812 let sel = e.selector().ok_or_else(|| BuildError::Missing {
2813 what: "index of an element select".into(),
2814 })?;
2815 if let Some(i) = sel.constant_value().and_then(|c| c.as_i64()) {
2816 return Self::static_window(base, &geom, i, i, "element select", ty);
2817 }
2818 let (idx, wide) = self.wide_index(sel)?;
2819 let pos = if geom.descending() {
2820 bin(BinOp::Sub, idx, iconst(wide, geom.right), wide)
2821 } else {
2822 bin(BinOp::Sub, iconst(wide, geom.right), idx, wide)
2823 };
2824 Ok(Self::dyn_window(base, pos, wide, geom.ew, ty.width, ty))
2825 }
2826
2827 fn range_select(&mut self, e: Expression<'_>, ty: Ty) -> Result<Expr, BuildError> {
2828 let (base, geom) = self.select_base(e)?;
2829 let kind = e
2830 .range_selection_kind()
2831 .ok_or_else(|| BuildError::Missing {
2832 what: "kind of a range select".into(),
2833 })?;
2834 let (l, r) = match (e.range_left(), e.range_right()) {
2835 (Some(l), Some(r)) => (l, r),
2836 _ => {
2837 return Err(BuildError::Missing {
2838 what: "bounds of a range select".into(),
2839 });
2840 }
2841 };
2842 let const_of = |x: Expression<'_>| x.constant_value().and_then(|c| c.as_i64());
2843 if kind == 0 {
2844 let (Some(a), Some(b)) = (const_of(l), const_of(r)) else {
2845 return Err(BuildError::Missing {
2846 what: "constant bounds of a `[l:r]` select".into(),
2847 });
2848 };
2849 return Self::static_window(base, &geom, a.min(b), a.max(b), "range select", ty);
2850 }
2851 let w = const_of(r)
2852 .filter(|w| *w >= 1)
2853 .ok_or_else(|| BuildError::Missing {
2854 what: "constant width of an indexed part select".into(),
2855 })?;
2856 let (dlo, dhi) = if kind == 1 { (0, w - 1) } else { (-(w - 1), 0) };
2858 if let Some(b) = const_of(l) {
2859 return Self::static_window(base, &geom, b + dlo, b + dhi, "part select", ty);
2860 }
2861 let (idx, wide) = self.wide_index(l)?;
2862 let pos = if geom.descending() {
2863 bin(BinOp::Add, idx, iconst(wide, dlo - geom.right), wide)
2864 } else {
2865 bin(BinOp::Sub, iconst(wide, geom.right - dhi), idx, wide)
2866 };
2867 Ok(Self::dyn_window(base, pos, wide, geom.ew, ty.width, ty))
2868 }
2869}