GCC 9 makes C diagnostics more specific
GCC 9.1 adds experimental C2x mode, but its more consequential changes for existing C code concern alignment, strings and the information carried by diagnostics.
Taking the address of a packed member can turn a declaration that the compiler understands into a pointer whose alignment its consumer cannot safely assume. GCC 9 adds a default warning for that operation. It is a useful example of the broader change in GCC 9.1, released May 3. The compiler reports more of the assumptions that disappear when C values cross an interface.
The release also introduces experimental C2x support. That is a much narrower claim than a new language implementation. The GCC 9 changes identify the only supported C2x addition as the single-argument form of _Static_assert. For existing programs, improved diagnostics can affect considerably more source code than that new mode.
I like C's small set of concepts, but compact syntax does not make its preconditions obvious. Alignment and the distinction between an array and a terminated string can each disappear behind a pointer parameter. Compiler diagnostics are valuable when they identify the precise operation at which that information is lost.
The pointer outlives the packed declaration
The new -Waddress-of-packed-member warning is enabled by default. It concerns an address that can be unaligned because the containing structure or union uses packed layout. The compiler may know how to access a packed member directly, while a function receiving an ordinary pointer sees a different interface.
A small declaration makes that difference concrete. The problematic operation below is returning a pointer to the member:
struct __attribute__((packed)) record {
unsigned char kind;
int count;
};
int *count_address(struct record *record)
{
return &record->count;
}
The warning does not say that every execution faults on every target. It says that the resulting pointer can fail the alignment requirement normally associated with its type. A caller that passes it onward has lost the packed-member context that informed the original access. Hardware tolerance for an unaligned instruction does not provide a portable source-level contract.
The GCC 9.1 warning manual documents this diagnostic separately from cast-alignment warnings. That distinction matters because no explicit pointer cast appears in the example. A conversion review limited to suspicious casts would miss the address expression itself.
The design implication is that a packed data representation should have an interface that acknowledges how it stores values. A direct member access and an exported ordinary pointer are different promises to a caller. The new diagnostic identifies the latter promise without requiring a programmer to discover it through an architecture-specific failure.
An array of characters is not necessarily a string
GCC 9 extends several diagnostics for formatted input and output. The changes include detection of some reads beyond unterminated constant character arrays when a %s directive expects a string. It also expands -Wstringop-truncation to detect some such reads. These are deliberately limited claims about detectable cases.
An array bound gives a compiler useful information about accessible bytes. A string operation imposes another condition, namely the presence of a terminator within the bytes the operation may examine. A declaration can satisfy the first condition while failing the second. The array's element type alone cannot express the difference.
For example, the following code deliberately constructs an array with no terminating null character. Passing it to an unbounded string conversion asks the conversion to obtain information that the declaration does not provide:
#include <stdio.h>
void print_bad_tag(void)
{
const char tag[3] = { 'r', 'e', 'd' };
printf("%s\n", tag);
}
A bounded presentation can express the actual size when that is the intended format. The precision in this example limits the conversion to the three supplied characters:
#include <stdio.h>
void print_tag(void)
{
const char tag[3] = { 'r', 'e', 'd' };
printf("%.3s\n", tag);
}
The examples differ in their contract with the formatted output function. Adding a terminator would be another design when the value is meant to be a C string. Selecting between those designs requires knowing whether the input represents a fixed-width field or a terminated string. A warning can expose the mismatch, but the data format determines the appropriate correction.
The warning manual describes the role of optimization in diagnostics such as -Warray-bounds. Its default level joins -Wall with optimization at -O2. This connection is useful when explaining why the compiler reports a problem in one build configuration and not another. Optimization can make object sizes or value ranges visible to the analysis that produces a warning.
A language mode with one implemented addition
Experimental -std=c2x selects support for the developing revision of ISO C. The GNU variant, -std=gnu2x, also admits GNU extensions. According to the release notes, the new C2x capability is a static assertion with its diagnostic string omitted.
An assertion about a compile-time property can therefore be written in the experimental mode as follows. The condition is still required to be an integer constant expression:
#include <limits.h>
_Static_assert(CHAR_BIT >= 8);
The existing C11 form supplies a message as its second argument. A project requiring C11 can continue to express the same property without selecting a developing standard:
#include <limits.h>
_Static_assert(CHAR_BIT >= 8, "the byte must contain at least eight bits");
This change removes an argument from a construct already present in the language. It does not imply support for every proposal under discussion in WG14. The release also adds -Wc11-c2x-compat to diagnose use of features added beyond C11, with related pedantic behavior outside the new modes. Language selection and compatibility diagnostics thus give a project separate ways to state what it accepts.
I prefer this explicit description of experimental support. Naming a future standard in a compiler option can otherwise suggest a completeness that the release does not claim. A build configuration has to reflect the particular feature it needs, especially while the language specification itself remains under development.
Diagnostics can be consumed by other tools
The release introduces -fdiagnostics-format=json. This gives a tool a structured representation of diagnostics instead of requiring it to recover every relationship from terminal text. GCC's general diagnostic changes also add line-number margins and labels for relevant regions of source.
The distinction between a primary diagnostic and its supporting notes matters to editor integrations. A complaint about a call can refer to the argument at the call site and the parameter in a declaration. Treating those messages as unrelated text lines discards the explanation. The release's diagnostic grouping support lets related messages appear together in the JSON output.
This is a practical compiler interface change. A human reading a build log and a program annotating an editor buffer need different presentation formats. Structured output allows the integration to retain locations and relationships while choosing its own visual presentation. It also reduces dependence on punctuation or spacing intended for a terminal.
The same release adds -fsave-optimization-record, which writes a compressed JSON record of optimization decisions. The notes describe metadata including inlining context and profile information. That record answers a different question from a warning. A warning describes a questionable construct, while an optimization record explains a transformation the compiler considered or performed.
Compound literals retain their block lifetime
The GCC 9 porting guide documents a correction to block-scope compound literals. Older releases gave these literals a lifetime extending to the end of the containing function. GCC 9 associates their automatic storage duration with the enclosing block, as the standard requires.
That matters when a pointer escapes an inner block. Keeping the pointer in a function-scope variable does not extend the lifetime of the object it once designated. The declaration of the receiving pointer and the creation of the compound literal belong to different scopes. An access later in the function can consequently occur after the object's lifetime has ended.
The appropriate source change follows the required lifetime. A compound literal needed throughout the function belongs in a scope that remains active for that use. This correction concerns existing C semantics, so selecting experimental C2x mode is unrelated to resolving it.
Choosing the appropriate numeric operation
A new C diagnostic, -Wabsolute-value, warns when a more appropriate standard absolute-value function exists for an argument. The release notes use an integer absolute-value function called with a double as an example. The issue is a numeric conversion at a valid function call, so a declaration alone does not make the operation communicate its likely intent.
The warning also covers unsigned arguments and is enabled by -Wextra. Its focus is specific enough to be useful in review. A call can compile while discarding the fractional part that the programmer expected to preserve. Choosing the corresponding floating-point function states that intent directly.
These diagnostics fit the C language I find useful for interoperation. Interfaces remain compact, and the compiler gains more ways to identify assumptions that a compact signature cannot encode. GCC 9's packed-member warning locates a possible alignment loss at the address expression. Its string diagnostics locate a mismatch between known storage and the operation that consumes it. Those locations are the information needed to decide whether the declaration, representation or consuming operation should change.