[Clang] Do not emit exception diagnostics from coroutines and coroutine lambdas
authorDeniz Evrenci <denizevrenci@gmail.com>
Tue, 28 Feb 2023 10:36:46 +0000 (11:36 +0100)
committerNikita Popov <npopov@redhat.com>
Tue, 28 Feb 2023 10:41:07 +0000 (11:41 +0100)
All exceptions thrown in coroutine bodies are caught and
unhandled_exception member of the coroutine promise type is called.
In accordance with the existing rules of diagnostics related to
exceptions thrown in functions marked noexcept, even if the promise
type's constructor, get_return_object, or unhandled_exception
throws, diagnostics should not be emitted.

Fixes #48797.

Differential Revision: https://reviews.llvm.org/D144352

clang/lib/Sema/AnalysisBasedWarnings.cpp
clang/test/SemaCXX/warn-throw-out-noexcept-coro.cpp [new file with mode: 0644]

index 07e17f9f71072eac20f870ffbc24bb6452cf3788..436743564c2586c5c59bbca918899cd01fd5bdcd 100644 (file)
@@ -2509,7 +2509,7 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings(
   // Check for throw out of non-throwing function.
   if (!Diags.isIgnored(diag::warn_throw_in_noexcept_func, D->getBeginLoc()))
     if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D))
-      if (S.getLangOpts().CPlusPlus && isNoexcept(FD))
+      if (S.getLangOpts().CPlusPlus && !fscope->isCoroutine() && isNoexcept(FD))
         checkThrowInNonThrowingFunc(S, FD, AC);
 
   // Emit unsafe buffer usage warnings and fixits.
diff --git a/clang/test/SemaCXX/warn-throw-out-noexcept-coro.cpp b/clang/test/SemaCXX/warn-throw-out-noexcept-coro.cpp
new file mode 100644 (file)
index 0000000..4d52bdc
--- /dev/null
@@ -0,0 +1,63 @@
+// RUN: %clang_cc1 -std=c++20 %s -fcxx-exceptions -fsyntax-only -Wexceptions -verify -fdeclspec
+
+#include "Inputs/std-coroutine.h"
+
+// expected-no-diagnostics
+
+template <typename T>
+struct promise;
+
+template <typename T>
+struct task {
+    using promise_type = promise<T>;
+
+    explicit task(promise_type& p) { throw 1; p.return_val = this; }
+
+    task(const task&) = delete;
+
+    T value;
+};
+
+template <typename T>
+struct promise {
+    task<T> get_return_object() { return task{*this}; }
+
+    std::suspend_never initial_suspend() const noexcept { return {}; }
+
+    std::suspend_never final_suspend() const noexcept { return {}; }
+
+    template <typename U>
+    void return_value(U&& val) { return_val->value = static_cast<U&&>(val); }
+
+    void unhandled_exception() { throw 1; }
+
+    task<T>* return_val;
+};
+
+task<int> a_ShouldNotDiag(const int a, const int b) {
+  if (b == 0)
+    throw b;
+
+  co_return a / b;
+}
+
+task<int> b_ShouldNotDiag(const int a, const int b) noexcept {
+  if (b == 0)
+    throw b;
+
+  co_return a / b;
+}
+
+const auto c_ShouldNotDiag = [](const int a, const int b) -> task<int> {
+  if (b == 0)
+    throw b;
+
+  co_return a / b;
+};
+
+const auto d_ShouldNotDiag = [](const int a, const int b) noexcept -> task<int> {
+  if (b == 0)
+    throw b;
+
+  co_return a / b;
+};