Clang 20 brings familiar C extensions into the C2y draft
Complex literal suffixes and case ranges show how existing compiler practice becomes standard C. Clang 20 changes diagnostics as well as conformance accounting.
A language feature can become standard without becoming new to its compiler. Clang 20 gives two examples in its C2y support. Complex literal suffixes and case ranges already existed as GNU extensions. The new development is their relationship to the next C draft, including the warnings that describe them to programmers. That changes how a project interprets a clean build even when the compiler has long accepted the underlying syntax.
The LLVM 20.1.0 release appeared on March 4. Its Clang release notes distinguish implementation work from conformance updates that adopt existing behavior. This is a useful distinction for C, where code accepted by a familiar compiler can remain outside the language version named in the build configuration.
I like C partly because the language remains small enough to reason about as a whole. The practical complication is knowing which language the compiler is accepting. An extension, a draft feature, and a published standard feature can have identical syntax while making different promises about portability. These changes deserve attention for that reason even if they add little new machinery to Clang itself.
Complex literals have a longer implementation history
Jens Gustedt's N3298 proposal, dated July 10, 2024, identifies the absence of complex literals as a gap in C's complex-number support. It describes existing suffix support in GCC, Clang, and XL C. The proposal adopts the established combinations of a complex suffix with a floating suffix instead of inventing another notation.
The complex suffix can be i, I, j, or J. A suffix identifies the imaginary component of a complex value whose real component is positive zero. The paper explains the inclusion of j through conventions used in engineering and signal processing. Supporting both spellings also agrees with the compiler practice the proposal describes.
For example, these declarations express the same imaginary component using two supported spellings. They illustrate the C2y proposal that Clang already accepts through its earlier extension support.
double _Complex a = 2.0i;
double _Complex b = 2.0j;
float _Complex c = 2.0fi;
The suffix is part of a literal token. It is different from the I macro discussed in the proposal. Gustedt records that the earlier discussion mixed those concepts, and that an objection to the original proposal concerned restrictions relative to existing compiler practice. N3298 follows the established suffix combinations more closely.
There is a corresponding limit to what the proposal changes. It preserves the behavior of string-to-number conversion functions by making their wording refer to real floating constants. Parsing a number from text must still stop before a following complex suffix character. Adding a literal form to the language does not make those existing conversion functions parse complex numbers.
Clang 20's release notes say -Wgnu-imaginary-constant no longer has an effect in C modes because the syntax is now a C2y extension in C. The warning still applies in C++ modes. That is a diagnostic classification change, not the first implementation of the suffixes. A project that previously relied on that particular warning to identify GNU-only syntax needs to understand the changed meaning.
Case ranges retain their established spelling
The release notes describe a similar transition for N3370 case range expressions. Clang already supported case ranges as a GNU extension. In C modes, -Wgnu-case-range consequently no longer has an effect, while the warning continues to apply in C++ modes.
The proposal uses a closed interval. Both bounds are included, so the following range represents the values two, three, four, and five. It does not give the range an ordinary value that can be stored in a variable.
int classify(int n)
{
switch (n) {
case 2 ... 5:
return 1;
default:
return 0;
}
}
N3370 discusses why the established ellipsis spelling has awkward lexical details. Dots can belong to a preprocessing number, so placing numeric bounds immediately next to the ellipsis can prevent the intended tokenization. Spaces around the operator avoid that problem. The paper records the committee's preference for existing practice over alternative spellings with their own grammar difficulties.
Closed bounds also matter for array indexing. A range ending at an array's element count includes the past-the-end index. The paper gives this exact distinction between selecting an element and potentially forming a past-the-end pointer. The concise syntax does not change which indices designate array elements.
The proposal keeps related ideas separate. It discusses array designator ranges and possible future slice syntax, but does not propose standardizing slices through this change. That boundary matters when reading the broader discussion. An explanation of possible reuse of a grammar form is not an implementation commitment for every use that the form could support.
Conformance work includes decisions to keep behavior
Other entries in Clang 20 make the same distinction explicit. N3364 adopts existing Clang behavior for translation-time floating initialization involving signaling NaNs. N3346 changes certain initialization situations from undefined behavior to constraint violations, again matching existing Clang practice. A conformance update can therefore mean that the specification now describes behavior the compiler already had.
There are real rejection changes too. The release notes say Clang now rejects register void parameters under N3344. Other qualifiers and storage-class specifiers on that void parameter form were already rejected. The result is narrower than a general change to all parameter declarations, and it is useful precisely because the notes identify the formerly accepted case.
Empty structures and unions provide another diagnostic distinction. N3341 makes their behavior implementation-defined in C2y. Clang still emits -Wgnu-empty-struct in C23 and earlier modes, but the warning no longer has an effect in C2y mode. Here the selected language version directly changes how Clang describes the same construct.
I value release notes that identify these differences because acceptance alone is a poor account of C portability. A successful compilation may depend on an older extension, or it may implement a draft rule that has not reached a published standard. In Clang 20, the useful new information is often the status of that acceptance. Complex suffixes and case ranges now have C2y conformance behind syntax that existing Clang users could already write.