Edit: Thanks for everyone's answer and replies. Language Lawyer's answer is technically the correct one so that's accepted, but Human-Compiler's answer is the only one that meets the criteria (getting 2+ points) for the bounty, or that is elaborated enough on the question's specific topic.
Full question
Is it defined behavior to have an object b
placed in the coroutine state
(by e.g. having it as a parameter,
or preserving it across a suspension point),
where alignof(b) > __STDCPP_DEFAULT_NEW_ALIGNMENT__?
Example:
inline constexpr size_t large_alignment =
__STDCPP_DEFAULT_NEW_ALIGNMENT__ * 2;
struct alignas(large_alignment) behemoth {
void attack();
unsigned char data[large_alignment];
};
task<void> invade(task_queue &q) {
behemoth b{};
co_await submit_to(q);
b.attack();
}
Explanation
When a coroutine is called,
heap memory for the coroutine state
is allocated via operator new.
This call to operator new
may take one of the following forms:
- passing all arguments passed to the coroutine following the size requested, or if no such overloads can be found,
- passing just the size requested.
Whichever form the call takes,
note that it doesn't use the overloads
accepting a std::align_val_t,
which are necessary to allocate memory
that must be aligned more than __STDCPP_DEFAULT_NEW_ALIGNMENT__.
Therefore, if an object whose alignment
is larger than __STDCPP_DEFAULT_NEW_ALIGNMENT__
must be saved in the coroutine state,
there should be no way to guarantee
that the object will end up properly aligned
in memory.
Experimentation
async f(): Assertion `reinterpret_cast<uintptr_t>(&b) % 32ull == 0' failed.
so it definitely doesn't work on GCC trunk
(11.0.1 20210307).
Replacing 32 with 16 (which equals __STDCPP_DEFAULT_NEW_ALIGNMENT__)
eliminates this assertion failure.
godbolt.org cannot run Windows binaries, but the assertion fires with MSVC on my computer as well.