December 28, 2017·4 min read·engineeringcstandardsdiagnostics

Making the C static assertion message optional

N2197 proposes a single-argument static assertion for C. The C++ history explains why omitting a redundant message is different from standardizing how a compiler prints the failed condition.

A static assertion can express its failure condition more clearly than an accompanying message that repeats the same expression. Requiring both can encourage redundant text that later disagrees with the condition. Aaron Ballman's N2197 proposal, dated December 28, asks C to permit the assertion without that message. It follows the single-argument form adopted in C++17.

The WG14 document log records the paper under harmonizing static assertions with C++. Its target audience is developers who share code between the languages. The proposal is a small syntax change, but the history behind it distinguishes two decisions, whether a message is required and whether the standard dictates the content of an omitted message.

I prefer a useful explanation over mandatory explanatory text. When a condition already states the relevant property, another string may add no information. A compiler should still diagnose failure, while an author should be able to reserve a custom message for the cases where it explains the surrounding requirement.

The declaration keeps its existing purpose

Ballman's proposed grammar retains the existing _Static_assert form with a constant expression and string literal. It adds a form containing only the constant expression. Both remain declarations, with no runtime check introduced by the proposal.

The wording still requires an integer constant expression. A nonzero value gives the declaration no effect. A zero value violates a constraint and requires a diagnostic, with the supplied string included when one exists. Omitting the message changes the diagnostic obligation's wording, not the condition under which the assertion fails.

The following illustrates the proposed single-argument syntax. It is a proposal example, not portable syntax promised by the currently published C standard.

#include <stddef.h>

struct header {
    unsigned char kind;
    unsigned char flags;
};

_Static_assert(sizeof(struct header) == 2);

If a representation requirement needs explanation, the existing message form remains available. An assertion about a structure's size might describe an external format assumption, for example, instead of repeating the exact size comparison. The proposal does not remove that ability or require authors to use the shorter form everywhere.

Ballman also says that the change should require only core-language wording. It should not need a change to the static_assert macro in <assert.h>. That matters for shared code because a declaration-level change can support the familiar header spelling without adding a separate facility with different behavior.

The C++ argument included competing designs

The cited predecessor is Walter E. Brown's N3928 paper from February 2014. Brown describes repeated requests for a default message and independently invented macros. A common technique stringized the assertion expression and passed the resulting text to the two-argument declaration.

That technique offered a convenient default, but it also mixed two goals. One was to stop requiring a handwritten message. The other was to specify that the diagnostic contain the tokens of the condition. The discussion recorded in N3928 includes objections to making token stringization a core-language responsibility.

Brown reports that the evolution group supported a direction in which the message could simply be absent. The proposed wording then required the diagnostic to include the string only if the programmer supplied one. That resolves the convenience problem while leaving the compiler room to choose how to describe a message-free failure.

N2197 uses that same distinction for C. Ballman reports a common generic diagnostic among several C++ compilers, but the proposed C wording does not promise one exact diagnostic string. Users should therefore distinguish the required failure report from an implementation's particular wording or presentation of the source expression.

The distinction also affects maintenance of diagnostic text. A separately written message can become stale after an expression changes, which Brown's discussion identifies as one motivation for the shorter form. Allowing omission removes that duplicated description in cases where it contributes nothing beyond the source condition.

Shared headers benefit from syntax agreement

The proposal's interoperability argument concerns headers that both languages compile. A single invariant can apply to the same structure or constant regardless of which language includes the header. Divergent assertion syntax adds another compatibility decision around that invariant.

Ballman identifies Clang's C extension as prior art. That demonstrates an implementation of the shorter form, but extension support does not make it a standard C requirement. The proposal asks the committee to establish a common language rule so that the syntax's status no longer depends solely on that implementation choice.

I find the limited scope sensible. The paper does not attempt a richer formatting language for compile-time diagnostics, although the C++ history records interest in more expressive messages. It keeps the existing condition and failure semantics, preserves explicit messages and permits an omitted string. That gives shared headers a simpler declaration when the expression already explains the invariant, while leaving a custom message available to explain a requirement that the expression alone cannot convey.