<table border="1" cellspacing="0" cellpadding="8">
<tr>
<th>Issue</th>
<td>
<a href=https://github.com/llvm/llvm-project/issues/88478>88478</a>
</td>
</tr>
<tr>
<th>Summary</th>
<td>
[coroutines] clang fails to run trivial ABI destructors for an "aborted" function call
</td>
</tr>
<tr>
<th>Labels</th>
<td>
clang
</td>
</tr>
<tr>
<th>Assignees</th>
<td>
</td>
</tr>
<tr>
<th>Reporter</th>
<td>
jacobsa
</td>
</tr>
</table>
<pre>
Here's a small program containing a coroutine (`Foo`) that starts to evaluate a function call expression but then destroys itself before actually making the function call:
```c++
#include <coroutine>
#include <cstdlib>
#include <iostream>
struct DestroySelfTag {};
// An eager coroutine result type that supports awaiting DestroySelf to
// cause the promise to destroy the coroutine frame.
struct MyTask {
struct promise_type {
MyTask get_return_object() { return {}; }
std::suspend_never initial_suspend() { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
void unhandled_exception();
void return_void();
auto await_transform(DestroySelfTag) {
struct Awaiter {
bool await_ready() { return false; }
void await_suspend(const std::coroutine_handle<> h) { h.destroy(); }
int await_resume() {
// We should never resume; we destroyed ourselves just after
// suspending.
std::abort();
}
};
return Awaiter{};
}
};
};
// A trivial ABI type that lets us know when it's created and destroyed.
struct HasDestructor {
HasDestructor() { std::cout << "created: " << this << "\n"; }
~HasDestructor() { std::cout << "destroyed: " << this << "\n"; }
};
// A function that we have a call expression for below in Foo. This just
// needs to accept the right types -- it should never actually be called.
void AcceptArgs(HasDestructor, int, HasDestructor) { std::abort(); }
// A coroutine that creates a HasDestructor object then destroys itself
// before doing anything with it.
MyTask Foo() {
AcceptArgs(HasDestructor(), co_await DestroySelfTag(), HasDestructor());
std::abort();
}
int main() {
Foo();
return 0;
}
```
When compiled with `-std=c++20 -O2 -fno-exceptions` ([Compiler Explorer](https://godbolt.org/z/bzG8xsMM9)), the program works as expected. It creates one `HasDestructor` object, then destroys it again when `std::coroutine_handle::destroy` is called:
```
created: 0x7ffd04aa2b6a
destroyed: 0x7ffd04aa2b6a
```
However, the program is miscompiled when we put the `[[clang::trivial_abi]] ` attribute on `HasDestructor` ([Compiler Explorer](https://godbolt.org/z/fPxn5jE5b)). In this case we fail to run the destructor, creating but then not ever destroying the `HasDestructor` object:
```
created: 0x7fff8f4da6b2
```
It doesn't seem like this should happen. There isn't extremely rigorous documentation on the semantics of `[[clang::trivial_abi]]`, but [what does exist](https://clang.llvm.org/docs/AttributeReference.html#trivial-abi) says "the convention is that the callee will destroy the object before returning". That makes sense, but _there is no function call here_. `AcceptArgs` is never actually called. Instead this should be treated the same as the following, which [does work correctly](https://godbolt.org/z/fe8EY9KM1):
```c++
MyTask Foo() {
HasDestructor(), co_await DestroySelfTag();
std::abort();
}
```
In a real codebase this bug could cause resources to be leaked when a coroutine is stopped early, e.g. due to cancellation.
</pre>
<img width="1px" height="1px" alt="" src="http://email.email.llvm.org/o/eJykWE1v4zwO_jXKhWjgyvk85NCk7U6xGOxid4AXewpoiY41laVAkpvmPexvX0h2nDjNdGfmBYLY0QdFPiQfUkHv1c4Qrdh0zaaPI2xCZd3qOwpbeBwVVh5XX8gR43MPCL5GrWHv7M5hDcKagMooswMEYZ1tgjIEjC_YLHu2ls0yxpcQKgzgA7rgIVigN9QNBgKEsjEiKGtARLH0vnfkffxdNAFCRQYk-eDs0YMKnnQJBZXWEaAIDWp9hBpf4_GhoqEwlj-w7JFlp-9Z1n4E4-v4aUd5rozQjSRg-aY3gOVPN-d9kFoVP5hV1gdHWJ-n07cPrhEBHlsz_k26_IY7YPM1mz-yfD3QkT8z_gwPBgh35C4AdeQbHSAc99SB2ez3NsKJB1QhAnBxAAQ7ECiw8ZQQ2jtbq_huT7im4fNBpcOaxgPNvx6_oX9NGqdh6MY7Wduk1HkW4LRjR2HrKDTObG3xnUSIYcGXcS2042cYID5O4mX0Xf7gG78nI7eG3siBMioo1Ntu9DdloT7g0UOpzAdRxtK7oH34fzIB4M0qCY2p0EhNctvuU9a0knqv9ks7FOL71ZLzQmyCbb25DQ6NL62rGV8M46Yz-bwLTt54iDvJXc8CFNbqTq4jlMePwJWoPX2wsVe-3XvGSljjwxnZPna2LRws37D8CarTIdW4i7Te8vM5w9OUCb2ivqnprOlwHUAX138Q-Mo2WkIbIt22fA0HOsU3SbCN86TfyMP3xgfAMpD7gcTOSmV24-sVvcFYWBdueDqJ6RH8mN3tig7zzl1XNDAUMZTxQ7aA4NSbQg0P65cLhtAUPDQeXo09wCEyqQqJw4UjDCQBjTyDNEz5L-hT3DUi2EFIDSYuIukiGpoAKQQ2wDjvzmL5Q8KYn6ZCpfzFMjbdmPgYxuB_f_W03pp43q-d9gm8fVVJsB4IKnyLpeu6YpXWQUHaHkAZeLZ2DN_iuTHmBhINkUxlEEWim0jATu2qlt493N2BCsPA7otdQenY3l8pQR-SnAe384wvrjDbxKyKj6vxKyCHMX2doD0U5zqRsGi9G7uCYcC0bH-zfA8EdqVc2tQ_mGOo4stBhQpU6CzsaknsJT7QwWeGJ1P4BoTdJk6BayI9Lbi57zKzP038K6QihdWozEddewMuJHdckN2QdupWLoX_EfEUtt4rTbKFic2yu6TfY9fW8Azu_sHhrjT2ri9Lns2y1JNN15t2u4On9722jhybPjK-qELY-2hlcszOysLqMLZux_jzn4w_F3_-bfHuv35ddujwzamdSG3gwbpXD-hjOpAIJMfwcg4PGzvCWTYEepbBqSnYfIgUwB0q07IWm2Wf1Jo4eqovswyU7_LjR91f-_OClrL3eVnKbILIixm20wMeubXgpnu-2ENM1mtslIda-bPbok0Hgn3b30bzUuO9FhrNrjWoI_QtFiq6Z_oYVwGG4FTRBAJrbuL5lzxc_vPdTL8_TYvWw2N4MS1vCvQUFS5R6charjFJcTlgmYRozN6-bzc2QCKvDs1Tk_5JJPySz8pFOZE4K_gnLnkJIC15w_g8gCeqQatXau3qCLbC_Z5M5GpyBKpbS-_BUU36GJk5Bp0HaUVTkwmYaoFtQfBUowlKeLDlz7ky3Yg2CSY2XR8ijUYVgd6VD7dclYSNtX6rO29JKzzjzw-ncPgXleTICBpXodaM592Zd_FMvgQfG17GedvomzcyyQLlWxJPwzFnCA5K68G9oCPyjqdbulJmxziPgGHkulfy4Ml4Olm1DR2SYOzV9S5ObMcRpwvmbrP2qs51RQ5ejA-EcuCxgiB0LUxyAdYUqSddAK3W9pA03MChUqKKICd8I0XF-uVIBH38yZygxdN_ln__ep9o-yeuk58Vq9-qT_n6-iLzeRG6mQQGEByhBmElFei7BCiaHYiEaHs_dORt4wSl3qQg0ISvJ8K6vNxHTwS735MEQqeP0Qoa78Ygm3SxFGgEaZ3yZDySq1wu8yWOaHU_v-fLbJIvZqNqRYuCL_n9ZDmdLZe55HMhi5yX0wWX86lYzkZqxTM-ySb3PJtNZnw2LrHMuZgvJzgt78ViziYZ1ah0nxsj5X1Dq8ViMl-MNBakffpTg_M2IWPn9zhyq7j-rmh2nk0yrXzwZwlBBZ3-CenN9ZF-0_7EgL6nwIum-0yFPnWBaGK-JU-RjH3oIA1GjdOrq9BToWqKsbA1489Rme5xt3e2rZHPybaY98m8_wUAAP__n8N6gQ">