Assertions 101
Table of Contents
1 Abstract
An assertion is an expression on program state that evaluates to true or false. Without side effects. Simple assertions are made up of Boolean expressions. Deeper assertions use First or Higher Order Logics. These notes are about using assertions in Java, C++, [TBD Scala and Python].
2 Program Process State
- Assertions are expressions yielding true/false on a process state.
- We should really say process state.
- So, what is a state?
2.1 Process State: Crudely
- Crudely, a full dump, including the portions of the state maintained by the OS for the process.
2.2 Process State: Refined
- A labeled collection of all variables and their values at a given
moment
- Include all scopes (global, local, …)
- Includes "external" objects: files, network connections, …
- Observers do not alter the process state
- Non-Observers (for lack of a consensus term)
- Before beginning: process state S' (S prime)
- After the operation: process state S (S no prime)
- Occasionally, S == S' (deep equality)
- Almost always, S != S'
3 What are Assertions?
- An assertion is an expression on a program state that we expect to evaluate to true. Without side effects.
- Assertions are predicates written using the syntax of the host programming language. All the variables are those of a program. We are allowed to write side-effect free Boolean functions to deal with for-all and there-exists quantifiers.
- Simple assertions are made up of Boolean expressions.
- Deeper assertions use First or Higher Order Logics.
- An assertion is placed at a control point.
4 Floyd-Hoare Triplets
- {P} S {Q} is known as the Floyd-Hoare Triplet, named after Turing Award winners Robert Floyd and Tony Hoare.
- P and Q are assertions. S is a block of code.
4.1 Semantics of {P} S {Q}
- This triplet stands for the following: Let t' be a state satisfying P. Execute S on t'. Suppose S terminates; let us call the resulting state t. Then, t satisfies Q.
- The {P} S {Q} yields a true/false value. There is no middle ground of "may be" or "undefined". We are assuming that there is no middle ground for each of P, Q and S.
- So what if S does not terminate? Cryptic, but then {P} S {Q} is not saying any thing about whether Q holds. We could not reach the control point where Q is stated.
- What if no state satisfies P? Again, {P} S {Q} is not saying any thing about the behavior of S. It may "work" or not. But, the Boolean value of the box [{P} S {Q}] is true just as [False => Any] is true.
- Note that the triplet is about partial correctness: It omits termination.
- In the context of C++/Java, the notation changes a little:
/*@ P */ S /*@ Q */
4.2 Pre-Conditions (aka Entry Assertions)
- Let S be a method. Every method has (i) a precondition P, and (ii) a post condition Q.
- Pre-condition P is expected to be true just before the method is entered.
- A pre-condition can talk about the arguments, and globals, but not about the local variables of the method.
4.3 Post-Conditions (aka Exit Assertions)
- Post-condition Q is expected to be true just before the method is returning.
- A post-condition can talk about the values of arguments at entry, and the values of globals at entry and exit, but not about the local variables of the method. We will invent some notation to describe the values at entry; e.g., old-x, or x.old.
- If the method is a function a post-condition can talk about the return value.
4.4 Weakest and Strongest
- Given two assertions A1 and A2, if A1 implies A2, we say that A1 is stronger than A2.
- For a given S, we prefer the weakest P.
- For a given S, we prefer the strongest P.
4.5 Design-by-Contract
- Imagine we are yet to develop S. Consider it a method.
- It is the responsibility of the caller to guarantee that a pre-condition holds. The method is expected to assume this without checking.
- It is the responsibility of the developer of S to guarantee that the post condition holds upon return from S.
- ../OODesign/design-by-contract.html
5 Loop Invariant
- Every loop has an assertion that describes the overall effect of the loop. We place it within the loop. It is called a loop invariant.
- For while-loops, the traditional location for the loop invariant is just-left-of the Boolean expression. (Just-right of the while reserved word.)
- Invariant means that the relationship given remains true every time control hits the location. Not that nothing changes. E.g., x > y + 2 can be an invariant, even though both x and y change.
- We wish to write the strongest assertion as the loop invariant.
6 Class Invariant
- Every class has an assertion CI that describes the relationships among its data members and public methods. This called the class invariant.
- CI is part of the pre- and post-condition of every public method M.
- The pre-condition of M will be (CI and R1)
- The post-condition of M will be (CI and Q1)
- The "and" shown above is typical.
- CI is not expected to hold before a constructor method.
- CI is expected to hold after a constructor method.
- We wish to write the strongest assertion as the class invariant.
7 Assertion Examples
Ex: Assertions for sorting:
{n >= 0} sorting-alg {sorted(a[0..n-1]) and perm(a, a')}
- Assertions, with Tiny Examples
- Practical-Advice on Writing Assertions
- The 3n+1 Termination Problem
- Assertions in Java
- Assertions in C++
8 The Meaning of Silence
- Consider {pre P:: x is an integer} S {post Q:: x is a
prime number}.
- Assume that this code S is part of a program that also uses an integer variable named y.
- The obligations of S are clear. At the end of it, the value that x has must be a prime number.
- But what about y? If the value of y was y0 before S, can we expect that y is equal to y0 after S? The pre- and post- were silent on y. So is S free to do what ever to y?
- Suppose S was x := exp. Do not jump to the conclusion that after all this is an assignment to x, therefore y could not change.
- From now on, our expectation is this: If we have a sequence of statements S, and its P and Q are silent with respect to (wrt) y, then y must remain as it was before/after S.
9 For All …
- There is an implicit for all in the assertions. E.g., when we
write n >= 0 in the entry assertions, it includes a
for all n
that you may give so that n >=0. The n that is bound here is taken in all subsequent assertions – in loop invariants and in the exit assertion.
10 "When" and "for how long" must an assertion be true?
- This question is important when we have threads/ processes.
- Consider the S and Q as above. Instantaneously after S finished, Q is true. Are we expecting that Q will remain true, say for another 60 secs?
- Consider S; S2. Let e1 be the ending time of S, let b2 be the beginning time stamp of S2. Recall that in all modern PLs, we must not assume that b2 == e1 + delta. Clearly, delta cannot be negative. We cannot say that for some d, delta < d.
- Concurrency literature talks about "interference". Without interference, we expect the post condition Q to hold good at least until S2 starts. In the presence of interference, we must not expect this.
11 References
- Gries, David, The Science of Programming, Springer, 2012 [shouldn't this be 1981?]. Highly Recommended.
- Alagic, Suad, and Michael A. Arbib. The Design of Well-Structured and Correct Programs. Springer Science & Business Media, 2013. Highly Recommended.