`atomic_fetch_add` and friends misbehave on `atomic_bool`
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
The `atomic_fetch_*` operations are not supposed to be applicable to `atomic_bool` and its relatives, per C23 7.17.7.5p1 (using N3320). There is similar language back to C11.
However, Clang as of 22.1.0 (and trunk on godbolt) currently not only accepts the following code under `-std=c23 -pedantic`, but (arguably) miscompiles it:
```
#include
atomic_bool b;
void foo(void) {
atomic_fetch_add(&b, 1);
}
```
[Try on godbolt](https://godbolt.org/#g:!((g:!((g:!((h:codeEditor,i:(filename:'1',fontScale:14,fontUsePx:'0',j:1,lang:___c,selection:(endColumn:1,endLineNumber:6,positionColumn:1,positionLineNumber:1,selectionStartColumn:1,selectionStartLineNumber:6,startColumn:1,startLineNumber:1),source:'%23include+%3Cstdatomic.h%3E%0Aatomic_bool+b%3B%0Avoid+foo(void)+%7B%0A++++atomic_fetch_add(%26b,+1)%3B%0A%7D%0A'),l:'5',n:'0',o:'C+source+%231',t:'0')),k:47.71438104771438,l:'4',n:'0',o:'',s:0,t:'0'),(g:!((h:compiler,i:(compiler:cclang2210,filters:(b:'0',binary:'1',binaryObject:'1',commentOnly:'0',debugCalls:'1',demangle:'0',directives:'0',execute:'1',intel:'0',libraryCode:'0',trim:'1',verboseDemangling:'0'),flagsViewOpen:'1',fontScale:14,fontUsePx:'0',j:1,lang:___c,libs:!(),options:'-O3+-pedantic+-std%3Dc23',overrides:!(),selection:(endColumn:1,endLineNumber:1,positionColumn:1,positionLineNumber:1,selectionStartColumn:1,selectionStartLineNumber:1,startColumn:1,startLineNumber:1),source:1),l:'5',n:'0',o:'+x86-64+clang+22.1.0+(Editor+%231)',t:'0')),k:52.28561895228561,l:'4',n:'0',o:'',s:0,t:'0')),l:'2',n:'0',o:'',t:'0')),version:4)
The resulting code on x86-64 is `lock inc byte ptr [rip + b]`; that is, it actually adds 1 to the value of `b`, which may result in it having the illegal value of `2`.
Even if Clang does want to allow `atomic_fetch_*` on `atomic_bool` as an extension, it shouldn't cause unexpected and undefined behavior like this. If it's to be supported at all, then `atomic_fetch_add(&b, 1)` should be equivalent to `atomic_exchange(&b, true)`, since adding `1` to either possible `bool` value results in a nonzero "truthy" value.
(Note that compound assignment like `b += 1`, which is required by the standard, used to also incorrectly add to `b`, which was the longstanding #33210. This is now fixed in trunk, and behaves equivalently to `b = true`, albeit less efficiently.)
For comparison, GCC gives an error:
```
operand type '_Atomic atomic_bool *' {aka '_Atomic _Bool *'} is incompatible with argument 1 of '__atomic_fetch_add'
```
[Try on godbolt](https://godbolt.org/#g:!((g:!((g:!((h:codeEditor,i:(filename:'1',fontScale:14,fontUsePx:'0',j:1,lang:___c,selection:(endColumn:27,endLineNumber:4,positionColumn:27,positionLineNumber:4,selectionStartColumn:27,selectionStartLineNumber:4,startColumn:27,startLineNumber:4),source:'%23include+%3Cstdatomic.h%3E%0Aatomic_bool+b%3B%0Avoid+foo(void)+%7B%0A++++atomic_fetch_add(%26b,+1)%3B%0A%7D%0A'),l:'5',n:'0',o:'C+source+%231',t:'0')),k:31.809587365142924,l:'4',n:'0',o:'',s:0,t:'0'),(g:!((h:compiler,i:(compiler:cg152,filters:(b:'0',binary:'1',binaryObject:'1',commentOnly:'0',debugCalls:'1',demangle:'0',directives:'0',execute:'1',intel:'0',libraryCode:'0',trim:'1',verboseDemangling:'0'),flagsViewOpen:'1',fontScale:14,fontUsePx:'0',j:1,lang:___c,libs:!(),options:'-O3+-pedantic+-std%3Dc23',overrides:!(),selection:(endColumn:1,endLineNumber:1,positionColumn:1,positionLineNumber:1,selectionStartColumn:1,selectionStartLineNumber:1,startColumn:1,startLineNumber:1),source:1),l:'5',n:'0',o:'+x86-64+gcc+15.2+(Editor+%231)',t:'0')),k:34.85707930152375,l:'4',n:'0',o:'',s:0,t:'0'),(g:!((h:output,i:(compilerName:'x86-64+gcc+15.2',editorid:1,fontScale:14,fontUsePx:'0',j:1,wrap:'1'),l:'5',n:'0',o:'Output+of+x86-64+gcc+15.2+(Compiler+%231)',t:'0')),k:33.33333333333333,l:'4',n:'0',o:'',s:0,t:'0')),l:'2',n:'0',o:'',t:'0')),version:4)
Contributor guide
Research direction
Start by reproducing the stdatomic.h example with Clang using -std=c23 -pedantic -O3, then trace the atomic_fetch_add handling for atomic_bool and compare it with the C23 restriction and GCC's diagnostic. Done means the operation no longer produces an illegal atomic_bool value: it is rejected or has the defined boolean behavior chosen by the project.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- c
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100