Authors: Joseph Myers
Date: 2025-03-06
Submitted against: C23
Status: Open
Cross-references: 0423, 0481
C23 6.7.3.5 (Atomic type specifiers) says "The properties associated with atomic types are meaningful only for expressions that are lvalues." and, similarly, in 6.7.4.1 (Type qualifiers), "The properties associated with qualified types are meaningful only for expressions that are lvalues.". Not being meaningful, however, leaves open the possibility that an rvalue might nevertheless have such a type (with no semantic difference compared to having an unqualified type).
Before C11, it was never possible to observe whether an rvalue had a
qualified type. C11 introduced _Generic
with the potential to
observe more details of the types of rvalues, and C23 introduced
typeof
, and these features resulted in a series of changes to avoid
rvalues having qualified or atomic types or to avoid such types being
observable:
The resolution of issue 0423 said that casts convert to the unqualified version of the named type, and that function types always return the unqualified version of the return type named in the declaration or type name.
The resolution of issue 0481 then dealt with
_Generic
on lvalues by saying the type of the controlling
expression is taken "as if it had undergone an lvalue conversion,
array to pointer conversion, or function to pointer conversion".
This left more ambiguous the case of any remaining rvalues with
qualified type: does "as if it had undergone an lvalue conversion"
remove qualifiers from the type of a qualified rvalue?
N2726
removed the const
qualifier from _Complex_I
and _Imaginary_I
.
The resolution of C23 CD1 comment GB-065 extended the resolution of issue 0423 to use the non-atomic versions of atomic types.
In June 2024, Aaron Ballman pointed out in reflector message 26025 that it was still possible to have a qualified rvalue when accessing a qualified member of an rvalue structure or union (the result of a function call, assignment expression, conditional expression or comma expression with structure or union type), leading to a long discussion continuing through July and into August. In that discussion, Alex Celeste pointed out (message 26098) that qualifiers on array element types (which before C23 were not considered to be qualifiers on the array type) should still be preserved.
Consistency with the direction of the sequence of changes listed above
would indicate that qualifiers and _Atomic
should not be preserved
in other cases. That suggests the following would be valid.
struct s1 { const int i; } f1();
struct s2 { const int a[1]; } f2();
struct s1a { _Atomic int i; } f1a();
struct s2a { _Atomic int a[1]; } f2a();
extern int i;
extern const int a[1];
extern _Atomic int aa[1];
extern typeof(f1().i) i;
extern typeof(f2().a) a;
extern typeof(f1a().i) i;
extern typeof(f2a().a) aa;
static_assert(_Generic(f1().i, int: 1, default: 0));
static_assert(_Generic(f2().a, const int *: 1, default: 0));
static_assert(_Generic(f1a().i, int: 1, default: 0));
static_assert(_Generic(f2a().a, _Atomic int *: 1, default: 0));
In C23 6.5.3.4 (Structure and union members), insert at the end of the first Semantics paragraph:
If the first expression is not an lvalue and the designated member does not have array type, the result has the unqualified, non-atomic version of the type of the designated member.
Comment from Issues list maintainer on 2025-06-27:
In reflector message
29935, Jens Gustedt
expresses concern about the removal of _Atomic
qualifiers.
The handling of array members in non-lvalue structures and unions may require updates in C2y to either the wording proposed in this issue or the wording for array element access added in the integration of N3517. See reflector message 29936.