Clang 19 separates C2y experiments from C23 conformance
The new C2y mode starts the next revision's implementation work. C23 constexpr objects and corrected alignas parsing show why a standard-mode flag alone does not describe all compiler behavior.
Supporting a new language mode and correcting an existing mode are different kinds of compiler work. LLVM 19.1.0, released on 17 September, does both for C. Clang introduces -std=c2y for experiments with the next revision while continuing to implement C23 facilities and repair conformance details. The combination is useful evidence that language support cannot be summarized by whether a compiler recognizes a particular option.
C's small definition appeals to me, but its declaration grammar still requires care. A compiler correction can change whether existing source is accepted even when no runtime algorithm changes. These changes deserve an explanation grounded in the language rule they implement.
C2y has a provisional version macro
The release-tagged Clang notes say that C2y mode sets __STDC_VERSION__ to 202400L. They explicitly warn that the value may change. Its immediate purpose is to distinguish this mode from C23. It is an implementation marker for an evolving language mode, without establishing what a future published revision will contain.
That qualification matters for conditional compilation. An exact comparison with a provisional number can express a dependency on one compiler's present behavior more narrowly than intended. A project needs to decide whether it wants a language-revision threshold or a particular facility. Those requirements are related but cannot always be represented by the same test.
The practical reason to retain the distinction is maintenance. If a project needs one operation, the supported compiler behavior for that operation is the relevant evidence. If it requires a whole language mode, the mode becomes part of the build contract. Conflating the two can expose source to unrelated experimental changes when the project only wanted one implemented feature.
constexpr objects address constant-expression requirements
Clang 19 implements N3018, the constexpr specifier for object definitions. The proposal explains why a constant object and an expression usable in a constant-expression context are distinct in C. Making an object unmodifiable does not automatically give its identifier every compile-time property required by the language.
Here is a small C23 example of the new object facility. The named value participates in both a static assertion and an array bound.
constexpr unsigned int record_limit = 64;
static_assert(record_limit > 0);
struct record_buffer {
unsigned char bytes[record_limit];
};
The point is the status of record_limit in those contexts. The example does not require a preprocessor replacement to communicate its value. Its declaration gives the object a type and an initializer, and the language permits the named constant where an appropriate constant expression is required.
N3018 discusses why changing existing const objects would have wider effects. Existing source can depend on the distinction between an ordinary const-qualified object and an integer constant expression. Changing that distinction could alter the interpretation of other constructs. Introducing an explicit specifier makes adoption a source-level choice.
This is a useful compatibility approach. A maintainer can add the new facility where its semantics are wanted, without assuming that every older declaration acquires different properties. The narrower change is easier to reason about when a header is shared across independently maintained consumers. Each use also makes the requirement on the client compiler visible.
N3018 also requires the initializer value to be representable without a change of value. That gives the declaration a diagnostic role alongside its use in constant expressions.
The scope remains object definitions. Importing expectations about C++ constexpr functions into this C facility would overstate the feature. The proposal deliberately separates the implemented object mechanism from a broader compile-time function facility. Familiar spelling does not remove the need to read the C contract.
I regard this as a worthwhile reduction in preprocessor dependence. A typed language entity can express a compile-time value directly. It still needs to fit the project's supported compiler set, particularly when placed in a public header.
alignas belongs to C's declaration grammar
The release notes also describe corrected C23 parsing for alignas. Clang previously treated it like a C++ attribute in contexts where C's rules differ. The correction accepts a placement after a member's type and rejects placements after a declarator or between struct and its tag. The notes include explicit old and new acceptance examples.
This is a conformance correction with a possible source impact. A declaration accepted by the previous compiler is not sufficient evidence that its placement was valid C23. When the parser becomes more precise, source using a previously accepted placement can require editing. The appropriate explanation is the grammar change, without assuming that stricter acceptance means a new runtime alignment policy.
It is also a reason to keep C and C++ examples distinct in documentation. Shared tokens can occur in different grammatical positions. A sample copied into a C header must satisfy C's declaration rules, even when the surrounding concept is familiar from C++. Toolchain acceptance is useful feedback, but the selected language remains part of the example's contract.
The release also adjusts C23 literals and types
Other changes in the same C23 section include treating binary integer literals as standard in C23 mode and correcting promotion of bit-precise bit-fields. The latter promotes such a field to its bit-precise type, instead of int. The notes also record support for #embed and changes to UTF-8 string literal types. Each is a separate implementation fact.
These details affect different layers of source interpretation. A diagnostic about an extension concerns whether syntax belongs to the selected revision. A promotion correction concerns which type participates in an expression. New resource-inclusion support concerns preprocessing. Describing all of them as generic C23 support loses the information maintainers need to understand a particular build change.
For a project adopting this release, the useful compatibility account names the construct it relies on and the behavior that changed. C2y mode provides a place for work on the next revision. The constexpr and alignas changes supply concrete C23 behavior in the current release. Keeping those statements separate lets source choose the facilities it needs without treating the experimental mode as a substitute for understanding them.