Document Number: N2209
Submitter: Martin Sebor
Submission Date: March 26, 2018
Subject: Atomic Pointers In Expressions

Summary

The current specification makes it a constraint violation to use pointers to objects of atomic types in certain contexts where they can, and in our view should be permitted. The following paragraphs detail the restriction and the contexts in which it applies.

In §6.2.5 Types, paragraph 27 reads:

Further, there is the _Atomic qualifier. The presence of the _Atomic qualifier designates an atomic type. The size, representation, and alignment of an atomic type need not be the same as those of the corresponding unqualified type. Therefore, this Standard explicitly uses the phrase "atomic, qualified or unqualified type" whenever the atomic version of a type is permitted along with the other qualified versions of a type. The phrase "qualified or unqualified type", without specific mention of atomic, does not include the atomic types.

The only occurrences of the phrase "atomic, qualified or unqualified type" in § 6.5 Expressions are in the following subsections:

Of the remaining contexts, the following expressions specify among their constraints that one or both pointer operands shall be "qualified or unqualified" versions of a type without mentioning atomic types:

Consequently, pointers to objects of atomic types are not valid operands in the expressions above. We believe these constraints are both unnecessary and unintended. For example, there is no reason for the following code to be rejected:

	extern _Atomic int a[2], *p;
	a[0] = a == p ? a[1] : a[0];
No implementation is known to enforce the constraint in these contexts.

The Simple assignment constraints are worth discussing in more detail. The relevant constraints are as follows:

One of the following shall hold:112)

— the left operand has atomic, qualified, or unqualified pointer type, and (considering the type the left operand would have after lvalue conversion) both operands are pointers to qualified or unqualified versions of compatible types, and the type pointed to by the left has all the qualifiers of the type pointed to by the right;
— the left operand has atomic, qualified, or unqualified pointer type, and (considering the type the left operand would have after lvalue conversion) one operand is a pointer to an object type, and the other is a pointer to a qualified or unqualified version of void, and the type pointed to by the left has all the qualifiers of the type pointed to by the right;

Atomic-qualified void types are permitted in paragraph 3 of §6.7.3 Type qualifiers:

The type modified by the _Atomic qualifier shall not be an array type or a function type.

A strict (perhaps pedantic?) reading of the constraints implies that although it is valid to assign an unqualified pointer to an atomic-qualified pointer object it is a constraint violation to assign an _Atomic pointer to an object of a compatible pointer type because the qualifiers allowed for the right operand do not include _Atomic (pursuant to §6.2.5 Types, paragraph 27 the phrase "qualified or unqualified type", without specific mention of atomic, does not include the atomic types). By way of an example:

	extern T *p;
	_Atomic T *ap, *aq;
	ap = p;                // unsafe but valid
	aq = ap;               // safe but constraint violation
The first assignment is unsafe because (unlike with other qualifiers) accessing a non-atomic object using an lvalue of an atomic-qualified type doesn't have defined semantics.

The above is so regardless of the type T. Provided atomic-qualified void pointers are intended to be allowed (not all implementations that support atomics accept them), this also affects the second bullet. Otherwise, if atomic-qualified void pointers are not intended to be permitted then only the fisrst bullet is affected. In that case, however, paragraph 3 of §6.7.3 Type qualifiers needs to be adjusted to exclude void from the set of types that may not be modified by the _Atomic qualifier.

Proposed Resolution

We propose to allow pointers to objects of atomic types in the contexts above. Specifically, we propose to make the following changes.

In §6.5.6 Additive operators make changes in paragraph 3 as indicated below.

For subtraction, one of the following shall hold:
— both operands have arithmetic type;
— both operands are pointers to atomic, qualified or unqualified versions of compatible complete object types; or
— the left operand is a pointer to a complete object type and the right operand has integer type.

In §6.5.8 Relational operators make changes in paragraph 2 as indicated below.

One of the following shall hold:
— both operands have real type; or
— both operands are pointers to atomic, qualified or unqualified versions of compatible object types.

In §6.5.9 Equality operators make changes in paragraph 2 as indicated below.

One of the following shall hold:
— both operands have arithmetic type;
— both operands are pointers to atomic, qualified or unqualified versions of compatible types;

In §6.5.15 Conditional operator make changes in paragraph 3 as indicated below.

One of the following shall hold for the second and third operands:

— both operands are pointers to atomic, qualified or unqualified versions of compatible types;
— one operand is a pointer and the other is a null pointer constant; or
— one operand is a pointer to an object type and the other is a pointer to a qualified or unqualified version of void.

In §6.5.16.1 Simple assignment make changes in paragraph 1 as indicated below.

One of the following shall hold:112)

— the left operand has atomic, qualified, or unqualified pointer type, and (considering the type the left operand would have after lvalue conversion) both operands are pointers to atomic, qualified or unqualified versions of compatible types, and the type pointed to by the left has all the qualifiers of the type pointed to by the right ,and either both operands have an atomic-qualified pointer type or neither does;
— the left operand has atomic, qualified, or unqualified pointer type, and (considering the type the left operand would have after lvalue conversion) one operand is a pointer to an object type, and the other is a pointer to an atomic, qualified or unqualified version of void, and the type pointed to by the left has all the qualifiers of the type pointed to by the right, and either both operands have an atomic-qualified pointer type or neither does;