Clang 16 makes qualifier removal explicit in C
The C2x typeof operators distinguish preserving a type from creating an unqualified working value, while stricter diagnostics reject older assumptions.
A declaration copied from another object can copy more than its numeric representation. It can also carry qualifiers that make the new object inappropriate as a mutable working value. Clang 16 implements two C2x operators that let the programmer express this choice directly. typeof preserves the operand's type, while typeof_unqual removes its qualifiers.
The LLVM 16.0.0 release was published on March 17. Its Clang release notes connect these operators to WG14 N2927 and N2930. The distinction is useful precisely because the two declarations can otherwise look almost identical. Type-generic code needs to say whether it is reproducing an object's access restrictions or creating a value for ordinary local manipulation.
I find this a sensible addition to C's vocabulary. The language remains understandable only if a short expression has a precise account of which type properties it retains. Explicit qualifier removal gives that decision a name instead of requiring the reader to infer the programmer's intention from a compiler-specific workaround.
Two operations with different results
The release notes describe typeof as accepting a parenthesized expression or a type name and producing the corresponding type with all qualifiers. typeof_unqual accepts a similar operand but removes all qualifiers. Clang explicitly includes atomic qualification and type attributes that behave like qualifiers, such as address-space attributes, in that removal.
A simple declaration demonstrates the distinction. The source object and each new object have separate initializers.
const int source = 7;
typeof(source) preserved = 9;
typeof_unqual(source) working = 9;
working = 10;
Here the declaration of preserved retains const, while working has unqualified type int. Assigning a new value to working is consistent with the purpose of a mutable local value. The example uses the C2x operators documented for Clang 16, so it belongs in that language mode.
This is a type operation, not a request to remove restrictions from the original object. The declaration of source still says const int. Creating another object with an unqualified type does not turn the original object into mutable storage. That distinction matters when reading helper declarations near code that handles externally supplied or restricted data.
Atomic qualification gives the rule additional practical weight. The notes show that removing qualifiers can produce an ordinary integer type from an atomically qualified operand. Code that needs another atomic object must preserve that property deliberately. A working value and shared atomic storage have different requirements, even if both contain integer data.
Clang's documented example also uses an address-space attribute. Preserving the type retains that attribute along with const and _Atomic, while the unqualified result is int. That makes the operation relevant to target-specific code as well as ordinary local variables. The compiler's rule extends beyond the qualifier most programmers first associate with the spelling.
Language mode is part of the interface
Clang 16 supports these operators only in C2x mode, according to the release notes. That qualification belongs beside any example using them. A compiler's version number does not by itself say which source-language rules a build selects.
A file named qualifiers.c can contain the declaration example. This command selects the intended language mode.
clang -std=c2x -fsyntax-only qualifiers.c
This command asks for a syntax check under the C2x mode. It does not establish that all of C23 is implemented or that a different compiler accepts the same program. The release notes document individual proposals, and that level of specificity is more useful than a general claim of support for a developing standard.
The naming history also matters. N2927 supplies the typeof work, while N2930 supplies the renaming associated with typeof_unqual. The release therefore implements an agreed spelling and a distinction between the operations. A project adopting the new syntax takes a dependency on that specific language facility.
For me, this is where C's small size remains useful. A type-preserving declaration and an unqualified declaration can be described separately without introducing an implicit conversion policy for every use. The resulting code still requires care, but the type choice is visible at the point where another object is declared.
Labels become less dependent on statement placement
The same release implements WG14 N2508, allowing labels everywhere inside a compound statement. The notes also connect WG14 N2662 to permission for [[maybe_unused]] on labels. These changes address the grammar surrounding control-flow destinations rather than changing what a jump means.
A label can now precede a declaration in C2x mode. This removes a placement restriction that can otherwise affect the structure of a block. The attribute supplies a separate way to express that a label may remain unused without producing the corresponding unused-label warning.
These changes have a common practical effect for generated or mechanically maintained code. A declaration and a label no longer require the same placement accommodations, and an intentionally available label can carry a local diagnostic annotation. Neither facility provides a new lifetime model or changes the need to understand the control flow.
Stronger errors accompany new syntax
Clang 16 also changes defaults for existing C code. The release notes say implicit function declarations and implicit int now produce errors by default in C99, C11, and C17. C2x removes support for those constructs, so the corresponding warning options cannot restore them there.
Incompatible function-pointer types also become an error by default in every C language mode. The documented example assigns a function taking const int * to a pointer to a function taking int *. These are different function types even though the parameter declarations may look closely related.
This affects configuration probes as well as application files. The notes explicitly warn that configure scripts can produce different results when diagnostics become errors. A probe that relied on an undeclared function can therefore stop recognizing an otherwise available facility. Its failure says something about the probe program before it says anything about the platform.
The release's C changes thus cover both new expression tools and stronger enforcement of existing type boundaries. Qualifier operators make the desired type relationship explicit in new code. Stricter diagnostics make previously tolerated assumptions fail earlier. For code that uses C as an interface language, both changes put more weight on declarations that describe the actual types involved.