Jens Gustedt's Blog

June 15, 2018

C17: rvalues and function types drop qualifiers

Filed under: C11, C17, defects — Jens Gustedt @ 21:15

At least since C99 the C standard has:

The properties associated with qualified types are meaningful only for expressions that are lvalues.

This phrase expresses the intent that type qualifiers are to be ignored in “some sense” if they appear in other contexts, namely for the values of expressions, i.e. rvalues.

Until the invention of _Generic in C11, nobody really cared what happened to qualifiers of expressions: only the value and size, not the type itself of an expression was observable from inside a C program.

This changed with C11, _Generic makes types observable. Unfortunately the formulation of the _Generic feature itself did not make it clear if it could be used to also detect the qualifications. C11 leaves the following questions unresolved

    1. Are there contexts where qualified rvalues can pop up?
    2. Can _Generic distinguish a qualified argument from an unqualified one?

In particular the lack of clarification of the second question lead to divergence in the implementation of the _Generic feature in mainline compilers.

In our work for C17, we tackled these questions in two “defect reports”, DR 423 (for a.) and DR 481 (for b.).

DR 423

For the first, there are two context that seemed to allow values of expressions to have qualifiers: casts and function returns. For both it was decided that the intent of the standard never had been to have qualifications here, and that the standard should be fixed to clearly state that intent. Therefore DR 423 proposes two modifications. For casts, it changes the first phrase of 6.5.4.p5 (change in red)

Preceding an expression by a parenthesized type name converts the value of the expression to the unqualified version of the named type.

For function returns, the situation is a bit more complicated, because the current syntax allows return types of functions to be qualified. E.g.

double const foo(void) {
    return 0;

Is a valid definition of a function. Whenever it is called, the return value is an rvalue, so is this value a double or a double const?

The answer that was found is to treat the above function as if the const where not present. That is, the return type of the function is adjusted to the unqualified type. The proposed change for p5 is

If, in the declaration “T D1“, D1 has the form
D ( parameter-type-list )
D ( identifier-list opt )
and the type specified for ident in the declaration “T D” is “derived-declarator-type-list T“, then the type specified for ident is “derived-declarator-type-list function returning the unqualified version of T“.

Beware that this has more effects than just changing the qualification of value expressions. Compilers that will claim to be compliant to C17 must accept the following:

double (*foop)(void) = foo;
double const (*foopc)(void) = foop;

That is any function type or type derived from that behaves as if the qualifier were not present. Most current compilers will probably see these as a constraint violations. Also, since now types that may have been considered different are now clearly specified to be the same, the following expression is a constraint violation:

    double (*)(void): 1,
    double const (*)(void): 2

because a generic expression is not supposed to have two entries with compatible types. Most current compilers probably would accept that construct, so they’d have to be fixed.

DR 481

For _Generic it was decided that the intent of the construct was to implement some kind of function overloading for C and that it was meant to have a behavior that is the closest possible to that intended feature. So it was decided to force that the type of a controlling expression is seen as if it would undergo the same conversions as arguments to function calls. The proposed text for reads

The type of the controlling expression is the type of the expression as if it had undergone an lvalue conversionnew), array to pointer conversion, or function to pointer conversion. That type shall be compatible with at most one of the types named in the generic association list.

new) lvalue conversion drops type qualifiers.


As a consequence the following code is still valid

    double: 1,
    double const: 2

only that the second association can never trigger. Therefore it would be reasonable for compilers to give a warning diagnostic for such a generic expression.

This does not mean, though, that qualifiers are not observable at all. You just have to put a little bit more effort into it:

    double*: 1,
    double const*: 2

This should still work and both associations may trigger depending on whether or not you pass an lvalue that is const qualified or not.


April 17, 2018


Filed under: C11, C17, defects — Jens Gustedt @ 08:48

C17 is a “bugfix release” of the C standard. Whereas the intention of the C working group (WG14) has been that this release does not introduce normative changes (but one), it brings a lot of clarifications all over the place. By adopting this version, some features as implemented by some compilers may change if their interpretation of C11 was different because of an unfortunate ambiguity. The schedule for C17 is as follows:

  • Nov. 2017, adoption by WG14, subject to some minor, editorial changes
  • Dec. 2017, integration of these changes and approval by an editorial committee
  • Jan. to Mar. 2018, editorial back and forth with ISO, more editorial changes due to new requirements by ISO and their strict enforcement
  • Apr 2018, ISO sends out the FDIS (final draft international standard) to the national bodies.
  • Apr to June 2018, ballot (ongoing)
  • June or July 2018, publication (tentative)

I identified the following list of changes in C17 compared to C11. The whole process of clarifications that have been integrated is transparently documented in what we called “defect reports”. So if you urgently need to know about some of these you should look them up, there.

My intention is to write a post on most of the items to explain my POV of what happened.  In particular, I will try to cite the new versions of the changed text for reference. Because of copyright issues, I will only be able to do that once C17 has been published by ISO. So please be patient and stay tuned.

    • atomics: initialization (7.17.2, 7.31.8), coherence requirements (, fences (7.17.3 p11), functions or macros (7.17.1 p6), lockfree depends on type (7.17.5 p3), compare exchange compares memory and not value (, atomic_flag (7.17.18)
    • threads: spurious wakeup ( p2, p2, p3), synchronization (7.26.4 p1), thread specific storage (tss_t) and thread exit (7.26.5 p3,, 7.26.6)
    • _Generic (, rvalues and qualification, cast (6.5.4)
    • alignment: fundamental alignment (6.2.8), _Alignas (6.7.5), aligned_alloc (
    • sequence points: full expressions (6.8 p4), full declarators (6.7..6 p3)
    • infinite loops (6.8.5 p6)
    • reserved identifiers (7.1.3)
    • domain or range errors (7.12.1), ilogb, erfc, lgamma,
    • underspecification of clock (
    • underspecification of realloc for size 0 (
    • Annex F: FLT_ROUNDS (F.2 p1)
    • Annex K: tmpnam_s, snprintf_s, sprintf_s, vsprintf_s, get_s, mbstowcs_s, wcstombs_s, snwprintf_s, swprintf_s, vsnwprintf_s, vswprint_s, mbsrtowcs_s, wcsrtombs_s

NB: comments are switched off for this post. Please communicate errors or imprecisions that you spotted to me directly. If on the other hand you want to discuss the future (or not) of C, there are a lot of places out there. The best that I know of is WG14 itself. So if you really care, please sign in on your committee of your national standards body for programming languages of alike, and invest yourself in the process.

Blog at