Compile-time Assertions
=======================

This module provides a header file verify.h that defines macros related to compile-time verification.

Two of these macros are verify (V) and verify_expr (V, EXPR).  Both accept an integer constant expression argument V and verify that it is nonzero.  If not, a compile-time error results.

These two macros implement compile-time tests, as opposed to the standard assert macro which supports only runtime tests. Since the tests occur at compile-time, they are more reliable, and they require no runtime overhead.

verify (V); is a declaration; it can occur outside of functions.  In contrast, verify_expr (V, EXPR) is an expression that returns the value of EXPR; it can be used in macros that expand to expressions.  If EXPR is an integer constant expression, then verify_expr (V, EXPR) is also an integer constant expression.  Although EXPR and verify_expr (V, EXPR) are guaranteed to have the same side effects and value and type (after integer promotion), they need not have the same type if EXPR's type is an integer that is narrower than int or unsigned int.

V should be an integer constant expression in the sense of the C standard.  Its leaf operands should be integer, enumeration, or character constants; or sizeof expressions that return constants; or floating constants that are the immediate operands of casts.  Outside a sizeof subexpression, V should not contain any assignments, function calls, comma operators, casts to non-integer types, or subexpressions whose values are outside the representable ranges for their types.  If V is not an integer constant expression, then a compiler might reject a usage like verify (V); even when V is nonzero.

Although the standard assert macro is a runtime test, C11 specifies a builtin _Static_assert (V, STRING-LITERAL), its assert.h header has a similar macro named static_assert, and C++11 has a similar static_assert builtin.  These builtins and macros differ from verify in two major ways.  First, they can also be used within a struct or union specifier, in place of an ordinary member declaration.  Second, they require the programmer to specify a compile-time diagnostic as a string literal.

The verify.h header defines one more macro, assume (E).  This macro expands to an expression of type void that causes the compiler to assume that the expression E yields a nonzero value.  E should be of a scalar type, and should not have side effects; it may or may not be evaluated.  The behavior is undefined if E would yield zero.  The main use of assume is optimization, as the compiler may be able to generate better code if it knows that E is true.

Here are some example uses of verify and verify_expr.

     #include <verify.h>

     #include <limits.h>
     #include <time.h>

     /* Verify that time_t is an integer type.  */
     verify ((time_t) 1.5 == 1);

     /* Verify that time_t is no smaller than int.  */
     verify (sizeof (int) <= sizeof (time_t));

     /* Verify that time_t is signed.  */
     verify ((time_t) -1 < 0);

     /* Verify that time_t uses two's complement representation.  */
     verify (~ (time_t) -1 == 0);

     /* Return the maximum value of the integer type T,
        verifying that T is an unsigned integer type.
        The cast to (T) is outside the call to verify_expr
        so that the result is of type T
        even when T is narrower than unsigned int.  */
     #define MAX_UNSIGNED_VAL(t) \
        ((T) verify_expr (0 < (T) -1, -1))

     /* Return T divided by UCHAR_MAX + 1.  Behavior is undefined
        if T is negative, and in the typical case where UCHAR_MAX
        is 255 the compiler can therefore implement the division
        by shifting T right 8 bits, an optimization that would
        not be valid if T were negative.  */
     time_t
     time_index (time_t t)
     {
       assume (0 <= t);
       return t / (UCHAR_MAX + 1);
     }