4.5. Operators
4.5.1. Arithmetic
+, -, *, unary -, /, %, /t, and %t operate on numeric values, and both operands
must have the same numeric type. For integers there are two division/remainder pairs, and the
difference only shows up on negative operands:
-
/and%are Euclidean: the remainder is never negative; -
/ttruncates the quotient toward zero, and%tisa - b * (a /t b).
procedure divisionFacts()
opaque
{
assert -7 / 3 == -3;
assert -7 % 3 == 2;
assert -7 /t 3 == -2;
assert -7 %t 3 == -1
};
All four forms generate a nonzero-divisor proof obligation, so a possible division by zero shows up as a failed precondition at the offending operator rather than as undefined behaviour. There is no source syntax for skipping that check.
Operators are not a separate kind of expression: each is a call to an overloaded built-in
procedure, which is why operand admissibility is overload selection and why / can carry a
precondition at all. That mechanism is described under Operators.
4.5.2. Boolean operators
procedure booleans(a: bool, b: bool)
opaque
{
assert (a & b) == (b & a); // eager: both sides evaluated
assert (a | b) == (b | a); // eager
assert (a && b) == (b && a); // b evaluated only when a is true
assert (a || b) == (b || a); // b evaluated only when a is false
assert (a ==> b) == (!a || b); // b evaluated only when a is true
assert !(!a) == a
};
The eager and short-circuiting pairs differ only when the right-hand side has an effect, which in
Laurel it may: operands can contain assignments and calls. The short-circuit forms mean exactly
what the corresponding conditional means — a && b is if a then b else false, a || b is
if a then true else b, and a ==> b is if a then b else true.
One caveat: if the right-hand side contains an assert or assume and nothing else effectful,
write the conditional explicitly rather than relying on the short-circuit form, because the proof
statement can currently escape its guard.
4.5.3. Equality, ordering, and strings
procedure comparisons(x: int, y: int, l: string, r: string)
opaque
{
assert (x == y) == !(x != y);
assert (x < y) ==> (x <= y);
assert (x > y) ==> (x >= y);
assert (l ^ r) == (l ^ r)
};
Equality requires operands of consistent types and is available at every type. Ordering requires
numeric operands. ^ concatenates two strings.
Equality means different things at different types, and the difference matters: on a datatype it is structural, and on a composite it is reference identity. See Aliasing and separation.