Consider the following C code:
#include <stdatomic.h>
struct _opaque;
typedef struct _opaque *opaque;
struct container { _Atomic opaque x; };
struct container get_container();
void consume_opaque(opaque);
void test() {
consume_opaque(get_container().x);
}
This fails to compile (Clang 16) with the error:
error: passing '_Atomic(opaque)' to parameter of incompatible type 'opaque' (aka 'struct _opaque *')
This code continues to fail to compile, even after trying to explicitly cast the argument: (opaque) get_container().x
.
By contrast, consider the following:
#include <stdatomic.h>
struct _opaque;
typedef struct _opaque *opaque;
struct container { _Atomic opaque x; };
struct container *get_container();
void consume_opaque(opaque);
void test() {
consume_opaque(get_container()->x);
}
This compiles perfectly fine and the compiler is apparently happy to convert get_container()->x
but not get_container().x
in the case where the container is returned by value.
Why is this the case? How can get the compiler to do the conversion in the first example without explicitly saving the return value of get_container()
to a local variable?
_Atomic opaque x
member with just a register to registermov rdi, rax
, not an atomic load from memory. Because the_Atomic
object only exists as a local temporary return value. In C, copying around whole structs does not respect stuff likevolatile
or_Atomic
on their members, so you've managed to bypass the things that normally stop you from making copies of an atomic object (like how in C++,std::atomic
has no copy constructor). – Meowstruct _opaque
apparently doesn't know that identifiers that start with an underscore at file scope are reserved: "All identifiers that begin with an underscore are always reserved for use as identifiers with file scope in both the ordinary and tag name spaces. " – Redd