1use super::ir::{Assignment, BinOp, ConstraintEvalError, Expr, StateId, Ty, UnOp, eval};
55use core::fmt;
56use num_bigint::BigUint;
57use num_traits::{One, Zero};
58
59#[derive(Clone, Copy, Debug, PartialEq, Eq)]
61pub enum GuardValue {
62 False,
64 True,
66 Error,
68 Random,
71}
72
73impl GuardValue {
74 pub fn and(self, other: GuardValue) -> GuardValue {
77 use GuardValue::{Error as E, False as F, Random as R, True as T};
78 match (self, other) {
79 (F, _) | (_, F) => F,
80 (E, _) | (_, E) => E,
81 (R, _) | (_, R) => R,
82 (T, T) => T,
83 }
84 }
85
86 pub fn or(self, other: GuardValue) -> GuardValue {
89 use GuardValue::{Error as E, False as F, Random as R, True as T};
90 match (self, other) {
91 (T, _) | (_, T) => T,
92 (E, _) | (_, E) => E,
93 (R, _) | (_, R) => R,
94 (F, F) => F,
95 }
96 }
97
98 #[allow(clippy::should_implement_trait)]
101 pub fn not(self) -> GuardValue {
102 match self {
103 GuardValue::False => GuardValue::True,
104 GuardValue::True => GuardValue::False,
105 GuardValue::Error => GuardValue::Error,
106 GuardValue::Random => GuardValue::Random,
107 }
108 }
109}
110
111#[derive(Clone, Debug, PartialEq, Eq)]
113pub enum GuardCause {
114 UnreadableState(StateId),
117 Eval(ConstraintEvalError),
119}
120
121#[derive(Clone, Debug, PartialEq, Eq)]
123pub struct GuardError {
124 pub cause: GuardCause,
126}
127
128impl fmt::Display for GuardError {
129 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
130 match &self.cause {
131 GuardCause::UnreadableState(s) => write!(
132 f,
133 "constraint guard error: state variable #{s} cannot be read (a null or deleted \
134 object handle, or an X/Z value)"
135 ),
136 GuardCause::Eval(e) => write!(f, "constraint guard error: {e}"),
137 }
138 }
139}
140
141impl std::error::Error for GuardError {}
142
143fn reads_var(e: &Expr) -> bool {
145 let mut any = false;
146 e.for_each_var(&mut |_, _| any = true);
147 any
148}
149
150fn unreadable_state(e: &Expr, a: &Assignment) -> Option<StateId> {
152 let mut bad = None;
153 e.for_each_state(&mut |s| {
154 if bad.is_none() && !matches!(a.states.get(s as usize), Some(Some(_))) {
155 bad = Some(s);
156 }
157 });
158 bad
159}
160
161fn leaf(e: &Expr, a: &Assignment) -> Result<GuardValue, GuardCause> {
163 if let Some(s) = unreadable_state(e, a) {
164 return Err(GuardCause::UnreadableState(s));
165 }
166 if reads_var(e) {
167 return Ok(GuardValue::Random);
168 }
169 match eval(e, a) {
170 Ok(v) if v.is_zero() => Ok(GuardValue::False),
171 Ok(_) => Ok(GuardValue::True),
172 Err(e) => Err(GuardCause::Eval(e)),
173 }
174}
175
176pub fn value(pred: &Expr, a: &Assignment) -> GuardValue {
179 match pred {
180 Expr::Binary(BinOp::LogAnd, l, r, _) => value(l, a).and(value(r, a)),
181 Expr::Binary(BinOp::LogOr, l, r, _) => value(l, a).or(value(r, a)),
182 Expr::Unary(UnOp::LogNot, x, _) => value(x, a).not(),
183 other => leaf(other, a).unwrap_or(GuardValue::Error),
184 }
185}
186
187fn cause(pred: &Expr, a: &Assignment) -> GuardError {
190 let erroring = |e: &Expr| value(e, a) == GuardValue::Error;
191 let inner = match pred {
192 Expr::Binary(BinOp::LogAnd | BinOp::LogOr, l, r, _) => {
193 if erroring(l) {
194 Some(&**l)
195 } else if erroring(r) {
196 Some(&**r)
197 } else {
198 None
199 }
200 }
201 Expr::Unary(UnOp::LogNot, x, _) if erroring(x) => Some(&**x),
202 _ => None,
203 };
204 match inner {
205 Some(i) => cause(i, a),
206 None => GuardError {
207 cause: match leaf(pred, a) {
208 Err(c) => c,
209 Ok(_) => unreachable!("`cause` is only asked of an erroring predicate"),
210 },
211 },
212 }
213}
214
215fn constant(b: bool) -> Expr {
216 Expr::Const(Ty::BIT, if b { BigUint::one() } else { BigUint::zero() })
217}
218
219fn simplify(pred: &Expr, a: &Assignment) -> Result<Expr, GuardError> {
222 match value(pred, a) {
223 GuardValue::True => Ok(constant(true)),
224 GuardValue::False => Ok(constant(false)),
225 GuardValue::Error => Err(cause(pred, a)),
226 GuardValue::Random => Ok(match pred {
227 Expr::Binary(op @ (BinOp::LogAnd | BinOp::LogOr), l, r, t) => Expr::Binary(
228 *op,
229 Box::new(simplify(l, a)?),
230 Box::new(simplify(r, a)?),
231 *t,
232 ),
233 Expr::Unary(UnOp::LogNot, x, t) => {
234 Expr::Unary(UnOp::LogNot, Box::new(simplify(x, a)?), *t)
235 }
236 leaf => leaf.clone(),
237 }),
238 }
239}
240
241pub fn resolve(e: &Expr, a: &Assignment) -> Result<Expr, GuardError> {
248 Ok(match e {
249 Expr::Const(..) | Expr::Var(..) | Expr::State(..) => e.clone(),
250 Expr::Guard(p, then, otherwise, t) => match value(p, a) {
251 GuardValue::True => resolve(then, a)?,
252 GuardValue::False => resolve(otherwise, a)?,
253 GuardValue::Error => return Err(cause(p, a)),
254 GuardValue::Random => Expr::Guard(
255 Box::new(simplify(p, a)?),
256 Box::new(resolve(then, a)?),
257 Box::new(resolve(otherwise, a)?),
258 *t,
259 ),
260 },
261 Expr::Unary(op, x, t) => Expr::Unary(*op, Box::new(resolve(x, a)?), *t),
262 Expr::Binary(op, l, r, t) => {
263 Expr::Binary(*op, Box::new(resolve(l, a)?), Box::new(resolve(r, a)?), *t)
264 }
265 Expr::Ite(c, x, y, t) => Expr::Ite(
266 Box::new(resolve(c, a)?),
267 Box::new(resolve(x, a)?),
268 Box::new(resolve(y, a)?),
269 *t,
270 ),
271 Expr::Resize(x, t) => Expr::Resize(Box::new(resolve(x, a)?), *t),
272 Expr::Concat(parts, t) => Expr::Concat(
273 parts
274 .iter()
275 .map(|p| resolve(p, a))
276 .collect::<Result<_, _>>()?,
277 *t,
278 ),
279 Expr::Slice(x, lo, t) => Expr::Slice(Box::new(resolve(x, a)?), *lo, *t),
280 Expr::AllDistinct(members) => Expr::AllDistinct(
281 members
282 .iter()
283 .map(|m| resolve(m, a))
284 .collect::<Result<_, _>>()?,
285 ),
286 Expr::Array(n) => {
287 let mut err = None;
288 let r = n.map_children(&mut |c| match resolve(c, a) {
289 Ok(x) => x,
290 Err(e) => {
291 err.get_or_insert(e);
292 c.clone()
293 }
294 });
295 if let Some(e) = err {
296 return Err(e);
297 }
298 Expr::Array(r)
299 }
300 })
301}
302
303#[cfg(test)]
304mod tests {
305 use super::GuardValue::{Error as E, False as F, Random as R, True as T};
306 use super::*;
307
308 const ALL: [GuardValue; 4] = [F, T, E, R];
309
310 const AND: [[GuardValue; 4]; 4] = [
313 [F, F, F, F], [F, T, E, R], [F, E, E, E], [F, R, E, R], ];
318 const OR: [[GuardValue; 4]; 4] = [
319 [F, T, E, R], [T, T, T, T], [E, T, E, E], [R, T, E, R], ];
324 const NOT: [GuardValue; 4] = [T, F, E, R];
325
326 #[test]
327 fn conjunction_matches_figure_18_3_in_every_cell() {
328 for (i, a) in ALL.iter().enumerate() {
329 for (j, b) in ALL.iter().enumerate() {
330 assert_eq!(a.and(*b), AND[i][j], "{a:?} && {b:?}");
331 }
332 }
333 }
334
335 #[test]
336 fn disjunction_matches_figure_18_3_in_every_cell() {
337 for (i, a) in ALL.iter().enumerate() {
338 for (j, b) in ALL.iter().enumerate() {
339 assert_eq!(a.or(*b), OR[i][j], "{a:?} || {b:?}");
340 }
341 }
342 }
343
344 #[test]
345 fn negation_matches_figure_18_3() {
346 for (i, a) in ALL.iter().enumerate() {
347 assert_eq!(a.not(), NOT[i], "!{a:?}");
348 }
349 }
350
351 #[test]
352 fn the_tables_do_not_depend_on_operand_order() {
353 for a in ALL {
355 for b in ALL {
356 assert_eq!(a.and(b), b.and(a));
357 assert_eq!(a.or(b), b.or(a));
358 }
359 }
360 }
361
362 fn u8t() -> Ty {
363 Ty::unsigned(8)
364 }
365 fn st(i: u32) -> Expr {
366 Expr::State(i, u8t())
367 }
368 fn var(i: u32) -> Expr {
369 Expr::Var(i, u8t())
370 }
371 fn k(v: u64) -> Expr {
372 Expr::constant(u8t(), v)
373 }
374 fn bin(op: BinOp, l: Expr, r: Expr) -> Expr {
375 let t = match op {
376 BinOp::Eq
377 | BinOp::Ne
378 | BinOp::Lt
379 | BinOp::Gt
380 | BinOp::LogAnd
381 | BinOp::LogOr
382 | BinOp::LogImpl => Ty::BIT,
383 _ => l.ty(),
384 };
385 Expr::Binary(op, Box::new(l), Box::new(r), t)
386 }
387 fn not(x: Expr) -> Expr {
388 Expr::Unary(UnOp::LogNot, Box::new(x), Ty::BIT)
389 }
390 fn guard(p: Expr, then: Expr, otherwise: Expr) -> Expr {
391 Expr::Guard(Box::new(p), Box::new(then), Box::new(otherwise), Ty::BIT)
392 }
393 fn states(v: &[Option<u64>]) -> Assignment {
394 Assignment {
395 vars: vec![],
396 states: v.iter().map(|x| x.map(BigUint::from)).collect(),
397 }
398 }
399
400 fn leaf_of(v: GuardValue) -> Expr {
404 match v {
405 T => bin(BinOp::Eq, st(0), k(1)),
406 F => bin(BinOp::Eq, st(0), k(2)),
407 E => bin(BinOp::Eq, st(1), k(0)),
408 R => bin(BinOp::Eq, var(0), k(3)),
409 }
410 }
411 fn env() -> Assignment {
412 states(&[Some(1), None])
413 }
414
415 #[test]
416 fn leaves_are_classified_by_what_they_read() {
417 let a = env();
418 for v in ALL {
419 assert_eq!(value(&leaf_of(v), &a), v, "leaf for {v:?}");
420 }
421 let both = bin(BinOp::Lt, var(0), st(1));
424 assert_eq!(value(&both, &a), E);
425 let div = bin(BinOp::Div, st(0), k(0));
427 let div = bin(BinOp::Eq, div, k(0));
428 assert_eq!(value(&div, &a), E);
429 assert_eq!(value(&st(0), &a), T);
431 assert_eq!(value(&k(0), &a), F);
432 }
433
434 #[test]
435 fn predicates_combine_by_the_tables_for_every_pair() {
436 let a = env();
437 for (i, x) in ALL.iter().enumerate() {
438 for (j, y) in ALL.iter().enumerate() {
439 let and = bin(BinOp::LogAnd, leaf_of(*x), leaf_of(*y));
440 let or = bin(BinOp::LogOr, leaf_of(*x), leaf_of(*y));
441 assert_eq!(value(&and, &a), AND[i][j], "{x:?} && {y:?}");
442 assert_eq!(value(&or, &a), OR[i][j], "{x:?} || {y:?}");
443 }
444 assert_eq!(value(¬(leaf_of(*x)), &a), NOT[i], "!{x:?}");
445 }
446 }
447
448 #[test]
449 fn false_dominates_error_and_true_dominates_error_unlike_left_to_right() {
450 let a = env();
451 let e_and_f = bin(BinOp::LogAnd, leaf_of(E), leaf_of(F));
453 assert_eq!(value(&e_and_f, &a), F);
454 let e_or_t = bin(BinOp::LogOr, leaf_of(E), leaf_of(T));
455 assert_eq!(value(&e_or_t, &a), T);
456 let t_and_e = bin(BinOp::LogAnd, leaf_of(T), leaf_of(E));
458 assert_eq!(value(&t_and_e, &a), E);
459 }
460
461 #[test]
462 fn a_false_guard_takes_the_else_arm_and_drops_an_erroring_then_arm() {
463 let a = env();
464 let bad_then = guard(leaf_of(E), k_bit(1), k_bit(1));
466 let else_arm = bin(BinOp::Eq, var(0), k(5));
467 let g = guard(leaf_of(F), bad_then, else_arm.clone());
468 assert_eq!(resolve(&g, &a), Ok(else_arm));
469 let imp = guard(leaf_of(F), bin(BinOp::Eq, var(0), k(5)), k_bit(1));
471 assert_eq!(resolve(&imp, &a), Ok(k_bit(1)));
472 }
473
474 fn k_bit(v: u64) -> Expr {
475 Expr::constant(Ty::BIT, v)
476 }
477
478 #[test]
479 fn a_true_guard_is_its_body_unconditionally() {
480 let a = env();
481 let body = bin(BinOp::Eq, var(0), k(5));
482 let g = guard(leaf_of(T), body.clone(), k_bit(1));
483 assert_eq!(resolve(&g, &a), Ok(body));
484 }
485
486 #[test]
487 fn an_error_guard_fails_with_the_erroring_leaf() {
488 let a = env();
489 let g = guard(leaf_of(E), k_bit(1), k_bit(1));
490 assert_eq!(
491 resolve(&g, &a),
492 Err(GuardError {
493 cause: GuardCause::UnreadableState(1)
494 })
495 );
496 let div = bin(BinOp::Eq, bin(BinOp::Div, st(0), k(0)), k(0));
500 let p = bin(
501 BinOp::LogOr,
502 bin(BinOp::LogAnd, div, leaf_of(F)),
503 leaf_of(E),
504 );
505 let g = guard(p, k_bit(1), k_bit(1));
506 assert_eq!(
507 resolve(&g, &a),
508 Err(GuardError {
509 cause: GuardCause::UnreadableState(1)
510 })
511 );
512 }
513
514 #[test]
515 fn a_random_guard_stays_with_its_decided_subtrees_folded() {
516 let a = env();
517 let p = bin(
521 BinOp::LogOr,
522 bin(BinOp::LogOr, leaf_of(R), leaf_of(F)),
523 bin(BinOp::LogAnd, leaf_of(E), leaf_of(F)),
524 );
525 let body = bin(BinOp::Eq, var(1), k(7));
526 let g = guard(p, body.clone(), k_bit(1));
527 let want_p = bin(
528 BinOp::LogOr,
529 bin(BinOp::LogOr, leaf_of(R), k_bit(0)),
530 k_bit(0),
531 );
532 assert_eq!(resolve(&g, &a), Ok(guard(want_p, body, k_bit(1))));
533 }
534
535 #[test]
536 fn a_random_guard_with_an_error_disjunct_fails() {
537 let a = env();
538 let p = bin(BinOp::LogOr, leaf_of(R), leaf_of(E));
540 let g = guard(p, k_bit(1), k_bit(1));
541 assert!(resolve(&g, &a).is_err());
542 }
543
544 #[test]
545 fn guards_nest_and_resolve_inside_other_nodes() {
546 let a = env();
547 let y = bin(BinOp::Eq, var(0), k(1));
549 let z = bin(BinOp::Eq, var(0), k(2));
550 let x = bin(BinOp::Eq, var(0), k(9));
551 let inner = guard(leaf_of(F), x, y.clone());
552 let outer = guard(leaf_of(T), inner, k_bit(1));
553 let all = bin(BinOp::LogAnd, outer, z.clone());
554 assert_eq!(resolve(&all, &a), Ok(bin(BinOp::LogAnd, y, z)));
555 }
556
557 #[test]
558 fn a_uniqueness_group_is_resolved_through_its_guards() {
559 let a = env();
560 let group = |m: Vec<Expr>| Expr::AllDistinct(m);
561 let g = guard(
563 leaf_of(T),
564 group(vec![var(0), var(1), st(0)]),
565 group(vec![var(0), var(1)]),
566 );
567 assert_eq!(resolve(&g, &a), Ok(group(vec![var(0), var(1), st(0)])));
568 let g = guard(
569 leaf_of(F),
570 group(vec![var(0), var(1), st(0)]),
571 group(vec![var(0), var(1)]),
572 );
573 assert_eq!(resolve(&g, &a), Ok(group(vec![var(0), var(1)])));
574 let g = guard(leaf_of(R), group(vec![var(0), var(1)]), k_bit(1));
576 assert_eq!(
577 resolve(&g, &a),
578 Ok(guard(leaf_of(R), group(vec![var(0), var(1)]), k_bit(1)))
579 );
580 let member = bin(BinOp::Add, var(1), k(1));
582 let wrapped = guard(leaf_of(T), member.clone(), k_bit(1));
583 let g = group(vec![var(0), wrapped]);
584 assert_eq!(resolve(&g, &a), Ok(group(vec![var(0), member])));
585 let g = group(vec![var(0), guard(leaf_of(E), var(1), var(0))]);
587 assert!(resolve(&g, &a).is_err());
588 }
589
590 #[test]
591 fn an_expression_without_guards_resolves_to_itself() {
592 let a = env();
593 let e = bin(
594 BinOp::LogImpl,
595 bin(BinOp::Lt, var(0), st(0)),
596 bin(BinOp::Eq, var(1), k(4)),
597 );
598 assert_eq!(resolve(&e, &a), Ok(e));
599 }
600}