<table border="1" cellspacing="0" cellpadding="8">
    <tr>
        <th>Issue</th>
        <td>
            <a href=https://github.com/llvm/llvm-project/issues/63818>63818</a>
        </td>
    </tr>

    <tr>
        <th>Summary</th>
        <td>
            destructors not always properly run when a coroutine is suspended and then destroyed
        </td>
    </tr>

    <tr>
      <th>Labels</th>
      <td>
            c++20,
            miscompilation,
            coroutines
      </td>
    </tr>

    <tr>
      <th>Assignees</th>
      <td>
      </td>
    </tr>

    <tr>
      <th>Reporter</th>
      <td>
          zygoloid
      </td>
    </tr>
</table>

<pre>
    [Example](https://godbolt.org/z/zezvTqY8v):

```c++
#include <coroutine>
#include <iostream>

struct coroutine {
  struct promise_type;
  std::coroutine_handle<promise_type> handle;
 ~coroutine() { handle.destroy(); }
};

struct coroutine::promise_type {
  coroutine get_return_object() {
    return {std::coroutine_handle<promise_type>::from_promise(*this)};
  }
 std::suspend_never initial_suspend() noexcept { return {}; }
 std::suspend_never final_suspend() noexcept { return {}; }
  void return_void() {}
  void unhandled_exception() {}
};

struct Printy {
  Printy(const char *name) : name(name) { std::cout << "Printy(" << name << ")\n"; }
  Printy(const Printy&) = delete;
 ~Printy() { std::cout << "~Printy(" << name << ")\n"; }
  const char *name;
};

int main() {
  [] -> coroutine {
    Printy a("a");
    Printy arr[] = {
      Printy("b"), Printy("c"),
 (co_await std::suspend_always{}, Printy("d")),
 Printy("e")
    };
  }();
}
```

When the coroutine is destroyed after being suspended, `a` is destroyed, but `arr[0]` and `arr[1]` are not. Clang does not in general properly create cleanups for non-exceptional control flow that occurs in the middle of an expression / an initializer. At least array initialization is missing cleanups here, but it'd be worth checking through all exception-only cleanups because most of them are probably incorrect. It looks like there are 15 places where we currently push an EH-only cleanup:

```
CGCall.cpp: pushFullExprCleanup<DestroyUnpassedArg>(EHCleanup, Slot.getAddress(),
CGClass.cpp: CGF.EHStack.pushCleanup<CallBaseDtor>(EHCleanup, BaseClassDecl,
CGClass.cpp: EHStack.pushCleanup<CallDelegatingCtorDtor>(EHCleanup,
CGCoroutine.cpp: EHStack.pushCleanup<CallCoroEnd>(EHCleanup);
CGDecl.cpp: pushDestroy(EHCleanup, addr, type, getDestroyer(dtorKind), true);
CGDecl.cpp: pushFullExprCleanup<IrregularPartialArrayDestroy>(EHCleanup,
CGDecl.cpp: pushFullExprCleanup<RegularPartialArrayDestroy>(EHCleanup,
CGException.cpp: pushFullExprCleanup<FreeException>(EHCleanup, addr.getPointer());
CGExprAgg.cpp:        CGF.pushDestroy(EHCleanup, LV.getAddress(CGF), CurField->getType(),
CGExprAgg.cpp: CGF.pushDestroy(EHCleanup, LV.getAddress(CGF), field->getType(),
CGExprCXX.cpp: .pushCleanupWithExtra<DirectCleanup>(EHCleanup,
CGExprCXX.cpp: .pushCleanupWithExtra<ConditionalCleanup>(EHCleanup,
ItaniumCXXABI.cpp: CGF.EHStack.pushCleanup<CallGuardAbort>(EHCleanup, guard);
MicrosoftCXXABI.cpp: CGF.EHStack.pushCleanup<ResetGuardBit>(EHCleanup, GuardAddr, GuardNum);
MicrosoftCXXABI.cpp: CGF.EHStack.pushCleanup<CallInitThreadAbort>(EHCleanup, GuardAddr);
```

This bug is not new with coroutines; the same thing happens with statement expressions:

```c++
int main() {
  Printy arr[] = { Printy("a"), ({ return 0; Printy("b"); }) };
}
```

... never destroys `arr[0]`. But it seems more pressing now that it's reachable from standard C++20 code.
</pre>
<img width="1px" height="1px" alt="" src="http://email.email.llvm.org/o/eJykWFtz2zoO_jX0CyYem65vD35wZDvN7GU653T3dJ88lAhL3FKklqSSuA_nt-9Adyd2m9NmHNsCoQ_ARxCALLxXqUHcsPk9m-9GogyZdZtv59Rqq-QotvJMa_sXkRca2XzH-CoLofBstmX8wPghtTK2OoytSxk_fKN__Pb0-X__WT0xvia1yY5N2vfFpH4ljN_Tq5bymTKJLiUCm0WJdbYMyiCb7a-tK-uDQ5H3y9W7D65MAnR3A1s28ADNWuFsrjwew7lANhusSnJztu3uPWbCSI1sFl3esod2ob35z95bvmJ8TVYbpbFEH5w91wtsdg9suWs8Xu46iOve1w4NrQ_j6YNMMRwdhtKZo43_i0no3WiVAWoFkv2FUGu9k7P5sVmpoLchU57iGYQAfWQ9mb70BRp5NPiEDpRRQQl9bKSNl8biS4JFqFjrvayxfwx6UuanIeHJKtkoHOn7gLhXSqWpOZLHGlpZc0X55p5-csqE83BHagnjq8QaHyDJhAPGt0bkWKHOtlB_X3Wi5f0wTctAB4HNImCcd2iM81ZM9w1UaL_mkaFvlyS88qS9XNRu7ECixjBM94Gx73v156-4dYWXjto3NCsTIBfKvM38uqjBHR3cq3WhJQBE7adonJpd0XCugSNeLjAGNDLO4waDRxfipBM3N1akH8WzUOFtfgv9LM6-Sa9XSLJFGoAN17FZ7_x7e1TbitRTelmdh_T-kaGBkOGAQeWhqW0oQZwCOohRmRQa91GSz2wxEWwxudAmeUx5spjUhE6opSwmIIzshdNW6BCMDWOItDApSIuerkEZSNGgE5pKeoFOnyFxKAJColGYsvBwsg6MNXfdkRWa0io4q-Gk7TOETASwSVI6T4AUYa6k1Aj2BMIAvhQOvVfWAOMHkjQ1TH1DN4ZtAI3CB0oMce7XBNmimHPlPXHSeZShwzZ-FRhfSogRnq0LGSQZJl9JO2TOlmkGQmvoXL-zhiJsgWJMROkRcusDORsyzCuuCmdjEWvyJrHOYRLG8BhAW_vVg1ZfkVQdVrrTORRaJOjhuZI9IySlc2iCPkNR-owi3n-8MH2rmdeX0UMktB4nBelVEIdS6_1L4aL29mhXJ8K_TCG8R7l1KXUavtp_bHV4BL9rG8Yphq2UtANNtra5Hj1EWnjf2okeDuP9x9-DSL6OyWZvi7y5Fx53wborVmipQtphom-h30beocZUBGXSKFh33UYH2Z6cH8OS6t7IN1j9aY0eyOEhzbtuzrgIUEjp6LPq5zyiUaHRRMf4Sgbr_qaob1a1KrgSv2_m7W4-OodpqYX7JBxl_5aOQuvNTTZ-jPvbT6Du28PyfeiDQ-xUr2QFkUa598kqEyqemlLb80KA2zRt7TR_lIff2Yy___syo6OHQ0N8VLqDQi2pS6UYPle79SrjX5n8aVun9xiKvnxpDQ0T9A8Vsv1LcIKOsaLi0rF6e0_ehRZZI1Vdon8A-RiEUWUeffmyvX98XwV4KIWT29i6cGW3U1oc7u4_VOKst6fwXhO_ocdQ2bhX1yzU5puTWF38s8x_ySIF9WhU-Jw5FDcjG9gdNPpr_f1zpjzEZUo9i5qrwWd4VtST2qrlaTqj_uhpfAsZ9alMFAUaX2v6IALmaMKgafp3Pfndnt1ujV0Xo47oZy0S9BP_hFy-NpU1c2Zla_fOCWg8HkP9wNHMMf7N_DKG-6qrg0fMPeS26sZYTwCmnTaqru_BoUgyEWsEeroi9owUTkJUM8MnkFiJ45HczOR6thYj3EwXq_WU8yVfjbLNab7m6ylynM6T6XSy4HydLBbLxUoms8UacaQ2fMJnk-WUTyYf5nw15suFFLGUJ3ma4wo_sA8TzIXSY62fcnpyHynvS9wsZqvpaqRFjNpXPwlUc2vtVEVgxDjPlU9sXigt6iehRjxIF87ZfDdyG0K_i8vUsw8TrXzwvb2ggsZNxWeZBOvq3KvH3n6oc6Wh8cSAuJw-uzmzmhsDaXQj5qh0evPqJwoVsjIeJzZn_EAuNB93hbP1M_Ohit8zfqgo-H8AAAD___BGRJk">