Discussion:
7.17.7.5 The atomic_fetch and modify generic functions and "address types"
Add Reply
Andrey Tarasevich
2024-12-27 22:06:33 UTC
Reply
Permalink
7.17.7.5 says (https://port70.net/~nsz/c/c11/n1570.html#7.17.7.5)
1 The following operations perform arithmetic and bitwise computations. All of these operations are applicable to an object of any atomic integer type. None of these operations is applicable to atomic_bool.
If I understand it correctly, this wording is intended to restrict these
operations to integers types only. I.e. they shall not be applied to
atomic pointer types. Is that correct?

Later 7.17.7.5 also says
3 For address types, the result may be an undefined address, but the operations otherwise have no undefined behavior.
However, I was unable to find any mention of "address types" anywhere in
the standard. What types are "address types"?
--
Best regards,
Andrey
Keith Thompson
2024-12-28 01:24:56 UTC
Reply
Permalink
Post by Andrey Tarasevich
7.17.7.5 says (https://port70.net/~nsz/c/c11/n1570.html#7.17.7.5)
1 The following operations perform arithmetic and bitwise
computations. All of these operations are applicable to an object of
any atomic integer type. None of these operations is applicable to
atomic_bool.
If I understand it correctly, this wording is intended to restrict
these operations to integers types only. I.e. they shall not be
applied to atomic pointer types. Is that correct?
Later 7.17.7.5 also says
3 For address types, the result may be an undefined address, but the
operations otherwise have no undefined behavior.
However, I was unable to find any mention of "address types" anywhere
in the standard. What types are "address types"?
I think you've found an error in the standard. It also appears in
the C17 and the N3096 and N3301 drafts.

There are no other occurrences of "address type" in the standard.
Presumably it was meant to be "pointer type", but pointer types
are already excluded.

An aside: The wording that excludes _Atomic bool is improved
in N3096:

The following operations perform arithmetic and bitwise
computations. All these operations are applicable to an object
of any atomic integer type other than _Atomic bool or the atomic
version of an enumeration with underlying type bool.
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+***@gmail.com
void Void(void) { Void(); } /* The recursive call of the void */
Tim Rentsch
2025-01-02 04:31:18 UTC
Reply
Permalink
Post by Andrey Tarasevich
7.17.7.5 says (https://port70.net/~nsz/c/c11/n1570.html#7.17.7.5)
1 The following operations perform arithmetic and bitwise
computations. All of these operations are applicable to an object of
any atomic integer type. None of these operations is applicable to
atomic_bool.
If I understand it correctly, this wording is intended to restrict
these operations to integers types only. I.e. they shall not be
applied to atomic pointer types. Is that correct?
I believe the intent is that the atomic_fetch_key() family of
functions may be called with either atomic integer types or atomic
pointer types (with the possible exception of atomic boolean types).

If the committee had meant to rule out atomic pointer types, the
standard could have given an explicit statement to that effect, like
what was done for atomic boolean types. The standard does not
include any explicit statement ruling out the use of atomic pointer
types; if anything atomic pointer types are implicitly included by
the parameter type name A, which is described in 7.17.1 paragraph 5
(paragraph 6 in some later versions of the standard).

Similarly, if the committee had meant to rule out atomic pointer
types, there is no reason for the last sentence in paragraph 3,
talking about what behaviors are allowed when address types are
used. Presumably the phrase "address types" is meant to be the same
as "pointer types" (see also below), which seems like the only
plausible interpretation.

There is no reason to think the inclusion of the last sentence of
paragraph 3 is an oversight. This sentence has survived unchanged
for more than 14 years, going back to at least 2010. But other
parts of section 7.17 were changed significantly during that time;
hence it seems unlikely that such an important incongruity was
missed over all of that time.

Why then does the standard take the trouble to say that these
operations are applicable to atomic integer types? I think the
confusion comes from reading "applicable" as "allowed". A more
consistent reading is for "applicable" to mean "well-defined". All
of the atomic_fetch_key() family of functions are well-defined for
all inputs of atomic integer type, even going so far as to stipulate
two's complement with wraparound for signed types (and this at a
time when the C standard did not require two's complement for signed
integer types generally). Atomic pointer types do not have this
property of producing well-defined results. Indeed, there is a
limited degree of undefined behavior that may result. In that sense
the operations of atomic_fetch_key() are not fully applicable to
atomic pointer types. But not applicable does not mean disallowed.
Post by Andrey Tarasevich
Later 7.17.7.5 also says
3 For address types, the result may be an undefined address, but the
operations otherwise have no undefined behavior.
However, I was unable to find any mention of "address types" anywhere
in the standard. What types are "address types"?
The phrase "address types" is a holdover from earlier drafts of the
C standard. It's instructive to compare N1547, a draft from
December 2010, to N1570, a draft from April 2011. The phrase
"address types" is used in several places, including to describe a
type name "atomic_address" (a struct type). I won't go through all
of the differences, which are fairly widespread. But looking them
over is I think convincing that pointer types were left in
deliberately.

A point that needs clarifying is whether bitwise operations are
allowed for atomic pointer type. The bitwise operations are allowed
by gcc, and disallowed by clang. I think an argument can be made
either way. Personally I think allowing bitwise operations is more
compelling than disallowing them, but I have to acknowledge the
possibility that the intent was place them off limits (meaning,
requiring a diagnostic). That said, I don't see any explicit text
in the standard that forces that conclusion.

Minor point: it is possible the gcc behavior a producing a '1'
difference in the example program posted in comp.lang.c is a
holdover from interpreting an earlier draft of the C standard, and
then not changing it after the actual standard was ratified. I
continue to think that what gcc did was wrong and what clang did was
right. If someone thinks otherwise I'd be interested to hear
whatever reasoning underlies their other position.

Disclaimer: my research was confined to looking at several drafts
of the C standard. I didn't make any effort to find or read DRs
that might be relevant, or any meeting notes or other official
documents that discuss or mention the issue.
Keith Thompson
2025-01-02 05:32:39 UTC
Reply
Permalink
Post by Tim Rentsch
Post by Andrey Tarasevich
7.17.7.5 says (https://port70.net/~nsz/c/c11/n1570.html#7.17.7.5)
1 The following operations perform arithmetic and bitwise
computations. All of these operations are applicable to an object of
any atomic integer type. None of these operations is applicable to
atomic_bool.
If I understand it correctly, this wording is intended to restrict
these operations to integers types only. I.e. they shall not be
applied to atomic pointer types. Is that correct?
I believe the intent is that the atomic_fetch_key() family of
functions may be called with either atomic integer types or atomic
pointer types (with the possible exception of atomic boolean types).
If the committee had meant to rule out atomic pointer types, the
standard could have given an explicit statement to that effect, like
what was done for atomic boolean types. The standard does not
include any explicit statement ruling out the use of atomic pointer
types; if anything atomic pointer types are implicitly included by
the parameter type name A, which is described in 7.17.1 paragraph 5
(paragraph 6 in some later versions of the standard).
Similarly, if the committee had meant to rule out atomic pointer
types, there is no reason for the last sentence in paragraph 3,
talking about what behaviors are allowed when address types are
used. Presumably the phrase "address types" is meant to be the same
as "pointer types" (see also below), which seems like the only
plausible interpretation.
I think the only plausible explanation is that "address types" refers to
atomic_address, a type that was specified in N1547 but dropped some time
before N1570 (though I'm not sure why it's plural.)
Post by Tim Rentsch
There is no reason to think the inclusion of the last sentence of
paragraph 3 is an oversight. This sentence has survived unchanged
for more than 14 years, going back to at least 2010. But other
parts of section 7.17 were changed significantly during that time;
hence it seems unlikely that such an important incongruity was
missed over all of that time.
Why then does the standard take the trouble to say that these
operations are applicable to atomic integer types? I think the
confusion comes from reading "applicable" as "allowed". A more
consistent reading is for "applicable" to mean "well-defined".
I think that's a bit of a stretch. If that's the intent, it should just
say "well-defined" (or just "defined"). The standard should say what it
means by "applicable", or it should be worded more clearly.

Given the current wording, I suppose applying atomic_fetch_add() to
atomic_bool would have undefined behavior. IMHO making it a constraint
violation would have made more sense (but constraints appear only in
section 6 of the standard).
Post by Tim Rentsch
All
of the atomic_fetch_key() family of functions are well-defined for
all inputs of atomic integer type, even going so far as to stipulate
two's complement with wraparound for signed types (and this at a
time when the C standard did not require two's complement for signed
integer types generally). Atomic pointer types do not have this
property of producing well-defined results. Indeed, there is a
limited degree of undefined behavior that may result. In that sense
the operations of atomic_fetch_key() are not fully applicable to
atomic pointer types. But not applicable does not mean disallowed.
Post by Andrey Tarasevich
Later 7.17.7.5 also says
3 For address types, the result may be an undefined address, but the
operations otherwise have no undefined behavior.
However, I was unable to find any mention of "address types" anywhere
in the standard. What types are "address types"?
The phrase "address types" is a holdover from earlier drafts of the
C standard. It's instructive to compare N1547, a draft from
December 2010, to N1570, a draft from April 2011. The phrase
"address types" is used in several places, including to describe a
type name "atomic_address" (a struct type). I won't go through all
of the differences, which are fairly widespread. But looking them
over is I think convincing that pointer types were left in
deliberately.
N1574 specifies a type "atomic_address", which didn't survive into C11
(good catch, BTW). N1574 7.17.7.5 says that the atomic_fetch_*
functions (only addition and subtraction, a distinction that doesn't
appear in N1570) are applicable to atomic_address, not to atomic pointer
types in general. I see no indication that the functions are meant to
be "applicable" to atomic pointer types.

The phrase "address types" should have been changed to "pointer types",
*if* that's what was intended.

Certainly atomic pointer types are valid. The question is which of the
atomic_fetch_* and atomic_modify_* function can be (meaningfully)
applied to them.

If the _add and _sub functions are applied to an atomic pointer type, is
the unit of addition or subtraction one byte or one element? N1547
explicitly says that it's one byte for atomic_address. Addition and
subtraction by elements is the only thing that makes sense for atomic
pointer types, but the standard doesn't specify it.
Post by Tim Rentsch
A point that needs clarifying is whether bitwise operations are
allowed for atomic pointer type. The bitwise operations are allowed
by gcc, and disallowed by clang. I think an argument can be made
either way. Personally I think allowing bitwise operations is more
compelling than disallowing them, but I have to acknowledge the
possibility that the intent was place them off limits (meaning,
requiring a diagnostic). That said, I don't see any explicit text
in the standard that forces that conclusion.
There's nothing in the standard to suggest that _add and _sub should be
valid for atomic pointer types but _or, _xor, and _and should not. But
IMHO there should be. Addition and subtraction of (atomic) pointer
types could be well defined; bitwise operations are not.

The simplest explanation is that this is a flaw in the standard.
It happens.

[...]
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+***@gmail.com
void Void(void) { Void(); } /* The recursive call of the void */
Loading...