Resolve/C++ FAQ: Run-Time Errors

This section addresses errors that happen at run-time. In other words, you must have already succeeded in compiling and linking the program, and now something goes haywire when you execute it.


Error 1: While loop that should terminate, but doesn't; or while loop body that shouldn't execute, but does 

Error 2: If or else clause that really should execute, but doesn't; or vice versa ("tautological if") 

Error 3: Bizarre return results from function calls 

Error 4: Short-circuit evaluation fails 

Error 5: Violated assertion.

Error 6: Preserves mode parameter partially or completely consumed 

Error 7: Wrong results/calculations (caused by repeated argments)

Error 8: Can't read from a Character_IStream that was just opened


Error 1: While loop that should terminate, but doesn't; or while loop body that shouldn't execute, but does

Diagnosis 1: When a program goes into an infinite while loop, it is because the while condition is always true. Most often this indicates a logical error in the code, i.e., the programmer "screwed-up", and needs to re-think and re-write the body of the loop to correct the error.

However, there is a special kind of syntax error, that is not caught by the compiler due to the general promiscuousness of C++, and instead shows-up at run time either as an infinite loop, or as a loop body that executes once even when it should not. This error is especially unpleasant because it is rather difficult to notice, since one usually devotes all the attention to the verification of the logic and the termination condition. Here is an example of this error (try to figure out what it is before reading the explanation):

while (((i < j) or (k >= 17)) and (not done));
{
i = j + k;
k = k + 1;
}

The error here is the semicolon after the last closing parenthesis in the loop condition. C++ allows you to open a new scope (by using '{' and '}') almost anywhere, and the while condition with ';' after it is perfectly OK too since an empty statement, i.e., ";", constitutes a perfectly valid loop body by itself. So with the spurious semicolon, the while condition always evaluates to the same thing since the objects involved in it are not changed in the (empty) loop body. If the while condition is true, the loop is infinite; if it is false, the loop terminates immediately but the body executes once anyway because it acts just like any other block of code following the loop.

See also Error 3 for another possible cause of an infinite loop.


Error 2: If or else clause that really should execute, but doesn't; or vice versa ("tautological if")

Diagnosis 2: When the body of an if statement is executed regardless of the value of the conditional, a very likely explanation is very similar to Error 1. For example:

if (i > 10);
{
i = 10;
}
will assign 10 to i regardless of the value of i before the if. Hence the name "tautological if", since "tautological" means "true regardless of the values of the variables"

See also Error 3 for another possible cause of this problem.


Error 3: Bizarre return results from function calls

Diagnosis 3: There are two common causes for this, one a client error and one an implementer error.

A very frustrating client error is to omit parentheses for operations with no parameters, e.g.:

while (q.Length > 0)
{...}

In this particular case, the loop either will be infinite or will result in a violated assertion, depending on what happens in the loop body (see also Error 1). If this were an if statement, then it would act as a tautological if (see also Error 2). And if it were a statement of the form:

object Integer len = q.Length;

then len would be assigned some ugly garbage value.

An equally frustrating implementer error is to forget to write a return statement that will execute before a function body returns -- or to have a return statement but to forget to write in it the expression to be returned. The function body compiles, but seemingly random results are returned by the function.

Unfortunately, there is no good method of preventing these errors other than trying hard not to make them.


Error 4: Short-circuit evaluation fails

Diagnosis 4: Many C/C++ books advertise short-circuit evaluation of if conditionals as an elegant and efficient way to code nested if statements, wherein the inner if should only be reached if the outer if conditional is true. For example, instead of the code segment:

if (b != 0)
{
if (a/b > 12) { ... }
}

In raw C++ one could write:

if ((b != 0) && (a/b > 12))
{
...
}

and rely on the fact that the raw C++ compiler will make sure that a/b will not be evaluated when b is 0, since then b != 0 is false, thus making the whole condition false.

However, in Resolve/C++ short circuit evaluation of Boolean-valued expressions is "disabled". The type of sub-expressions of while and if conditions is Boolean, and the operators and and or thus operate on objects of type Boolean (not on ints, as they do in native C++). So compound Boolean expressions connected by and and or will always be evaluated. For example, this means that the latter code segment will result in a precondition violation of the division operator if b is zero.


Error 5: Violated assertion

Example 5:

The program stops running and the following message is produced:

===================================================
Violated assertion at:

File: /class/sce/rcpp/RESOLVE_Catalog/CT/Queue/Kernel_C_Body.h
Line #: 32
Operation: Dequeue

Violated assertion is:
self /= empty_string
---------------------------------------------------

Diagnosis 5: All operations provided by the RESOLVE_Foundation, and all operations of checking components in the RESOLVE_Catalog, check their preconditions when they are invoked. If a precondition is not satisfied, an assertion violation occurs. The file, line number, and the operation name given by the assertion violation error message point to the operation whose precondition was violated. (Note: The file, line number, and operation name do not refer to the place where the call was made, but to the location of the operation body where the precondition was checked! Typically, only the operation name is of interest to a client programmer, since this tells you what operation you called with a violated precondition.) However, it is the fault of the client that the precondition was violated, and therefore it is the client's code that has to be corrected. It may be necessary to insert trace statements into the client's code, to pinpoint the specific invocation of the operation whose precondition was violated.

IMPORTANT NOTE: The line under "Violated assertion is:" in the message that comes out is a description of what should have been true but wasn't at the point of the call. It is not a description of what was actually true at the point of the call. So, in the example, the violated assertion "self /= empty_string" means self being non-empty was what should have been true at the time Dequeue was called, i.e., it is the precondition (requires clause) of Dequeue. The fact that is was not true when it should have been is why it's called a violated assertion.


Error 6: Preserves mode parameter partially or completely consumed

Diagnosis 6: In Resolve/C++, the way to traverse data structures, e.g., Queue, is to disassemble them into individual items, do whatever work needs to be done (e.g., searching) along the way, and then put the Queue back together. One handy way of doing this is to keep a catalyst object of the same type as the parameter object (in this case, Queue), into which the items coming out of the parameter Queue object will be inserted. Then, at the end of the operation, the catalyst object should be swapped with the parameter Queue object (which is now empty), to restore its original value.

In the heat of the battle, however, it is easy to forget about the catalyst object. The primary reason it is easy to forget about it entirely, or just to forget to swap it at the end, is because the catalyst has nothing to do with the main task the operation is supposed to perform. In fact, the restoration of the parameter object is not even mentioned in the post-condition proper, and is dictated instead by the implicit post-condition contained in the preserves mode of this parameter.

Therefore, when implementing an operation that needs to traverse a data structure, it is helpful to set-up the scaffolding of take-apart/store-in-catalyst/restore-by-swapping before writing the actual meat of the operation. This will guarantee that the implicit (and therefore harder to notice) parts of the post-condition are taken care of before the mind gets preoccupied with the main task at hand.


Error 7: Wrong results/calculations (caused by repeated argments)

Example 7:

Diagnosis 7:

Both of the above examples violate the "repeated argument restriction", i.e., the rule prohibiting repeated use of the same object as an actual parameter to one operation call. In the first example, n is both an implicit argument (before the dot) and an explicit one (in parentheses). In the second example, both of the actual parameters are the result of applying an accessor to the same Partial_Map object.


Error 8: Can't read from a Character_IStream that was just opened

Example 8:

Trying to open a Character_IStream object to get data from "standard input", you (mistakenly) write:

input.Open_External ();

The first time you try to read from the object input, you get a violated assertion for the Read operation from Character_IStream, saying that the object must be open in order to read. Didn't you just open it?

Diagnosis 8:

The problem is that you have to write this statement to connect input to "standard input":

input.Open_External ("");

The erroneous statement compiles because Open_External is overloaded. This means there is another version of Open_External that takes no arguments; it happens that this is used to read input from another program through Resolve/C++'s interprocess communication mechanism. If another program isn't trying to talk to your program at the point where you make the original call to Open_External, the object input is not opened; hence the violated assertion when you try to read from it.


Bruce W. Weide <weide@cse.ohio-state.edu>
Last modified: Fri Apr 29 22:44:33 EDT 2011