December 21, 2021·4 min read·engineeringcstandardsintegerssafety

Supplemental integer safety keeps arithmetic errors with the value

The December proposal adds checked integer objects and expression-friendly arithmetic forms. Carrying an overflow flag makes a failed intermediate operation visible to later checked operations.

Checking each arithmetic operation produces an additional value that ordinary arithmetic does not carry. The program needs the numeric result and an indication of whether that result was representable. Separating those values at every step makes a compound calculation cumbersome. Forgetting an intermediate flag can also make a later successful operation conceal an earlier failure.

The December 21 WG14 document-log entry links N2868, David Svoboda's supplemental integer-safety proposal. The document retains an older N2792 heading and October date, but its change log includes December 7 revisions. Its publication supplies a current proposal for checked integer objects, building on the core arithmetic operations accepted in June. The supplemental proposal itself is not presented as adopted.

I find the representation question central to this work. C can expose checked operations without changing ordinary operators, but a usable interface also has to preserve failure information between operations. A result object that includes that information gives a compound expression a defined place to keep it.

A checked object includes an overflow flag

The proposed family includes names such as ckd_int_t and ckd_size_t, corresponding to underlying integer types. Each checked type is a complete object type containing a value and an overflow flag in the abstract interface. An implementation may use a structure, but the proposal does not require that representation or make the contents directly addressable.

Construction uses named functions or the generic ckd_mk macro. ckd_value retrieves the underlying value, and ckd_overflow reports the flag. An initializer that assumes a particular internal structure is therefore inappropriate. The proposal explicitly excludes initializer-list construction of checked values even though assignment between objects of the same checked type is possible.

The flag's meaning includes overflow and conversion problems such as truncation or misinterpreting sign. Its name should not be read as limiting it to a processor's signed-overflow condition. The proposal's rationale considers hardware flags but chooses an arithmetic interface because not every supported machine exposes the same flag facilities.

When the flag is false, the object represents the mathematical result correctly. A true flag means later code must not interpret the numeric component alone as evidence of successful calculation. This distinction is particularly relevant when a wrapped intermediate value happens to fall within the range of a later destination.

Two arguments allow nested operations

The core checked interface takes a result pointer and two inputs. The supplement adds two-argument forms that return a checked object. An expression such as ckd_add(a, ckd_mul(b, c)) can then carry the multiplication's error information into the addition.

The proposed propagation rule reports an error if the current operation fails or if a checked input already records an earlier error. A later operation does not clear history simply because its own numeric result fits. This is the mechanism that makes nesting useful beyond reducing the number of local variables.

The two forms choose their destination types differently. In the result-pointer form, the pointed-to type specifies the result range. In the two-argument form, the underlying result type follows the usual arithmetic conversions. Returning a checked value therefore does not mean automatically choosing a width large enough for the mathematical answer.

That difference matters when refactoring a calculation. Replacing an explicitly wider result object with a nested expression can change which range determines success. The proposal calls out that assigning a checked result to a checked object of another type does not perform an implicit conversion. The programmer must respect the particular checked type selected by the operation.

Ordinary expressions remain ordinary

The supplement does not make an argument expression checked merely because its value enters a checked macro. Its discussion repeats the case of ckd_add(&result, x + y, z). The x + y operation still uses ordinary C arithmetic before the checked addition receives its argument.

Nested checked operations avoid that problem by making each intended operation explicit. They also preserve the chosen evaluation structure. If an intermediate mathematical result does not fit its selected type, the flag records that fact even if a differently grouped calculation could have avoided it. Error propagation does not make finite-range arithmetic obey every algebraic identity over mathematical integers.

I prefer that explicitness to an interface that silently discards intermediate failure. The programmer still chooses the calculation's types and grouping. The checked object supplies a way to retain the consequences of those choices until the application applies its error policy.

Bit-precise integers remain outside this proposal

The December revision addresses the interaction raised in N2867. Its wording excludes bit-precise integer types from the checked-operation arguments and result object categories. The prose says it continues the assumption that the facilities should remain separate until their interaction is better understood.

Implementation-defined checked types also receive a consistency requirement. If both operands have corresponding checked types, the underlying result type of their ordinary binary operation must have one too. That requirement supports the two-argument interface's conversion-based choice of result type. Without it, individually supported operands could combine into a result for which the implementation supplied no checked representation.