Clang error: 'A' cannot be used in the handler of a try block (err_coroutine_within_handler)

From emmtrix Wiki
Jump to navigation Jump to search
Text error: 'A' cannot be used in the handler of a try block (since 9.0)
Type Error
Category Coroutines Issue (since 9.0)
Internal Id err_coroutine_within_handler (since 9.0)
Internal Message '%0' cannot be used in the handler of a try block (since 9.0)
Regular Expression (?:error|fatal error)\: '(.*?)' cannot be used in the handler of a try block
First Commit 2019-03-15 9db9b1a17501 [coroutines][PR40978] Emit error for co_yield within catch block

Description

The error is issued by the Clang compiler when an attempt is made to use the co_await or co_yield keywords within the handler of a try block, also known as the catch block. This action contradicts the constraints laid out by the C++ coroutine specification, which requires these expressions to be used only in valid suspension contexts. A suspension context is defined as a context within a function where an await-expression can appear, typically outside of handlers and certain other restricted areas. The presence of co_await or co_yield within a catch block signifies an attempt to suspend the coroutine in a context where suspension is not allowed, leading to this error being raised. This restriction ensures the correct and consistent execution of coroutine semantics as defined by the C++ standard.  
AI Generated

Example

In the following example, an attempt is made to utilize the C++20 coroutine features within a try-catch block, which illustrates the violation of coroutines' restrictions regarding suspension points. The C++20 coroutine specification mandates that suspension points, marked by the use of co_await and co_yield, must only exist within valid suspension contexts. Specifically, these keywords cannot be employed within the catch section of a try-catch block, also referred to as the handler of a try block.

The example begins by defining a simple class A intended for use in the catch block. Following this, two structures, awaiter and B, are defined, with B being designed as an awaitable object through the definition of the operator co_await. The awaiter structure is utilized to define the behavior of this operation, signifying the suspension and resumption points of a coroutine.

A more complex case is introduced with the definition of structure C, which, similar to B, is awaitable but also includes a destructor. This nuance is critical because it leads to the crucial part of the example - demonstrating that co_await on an object of class C within a catch block is invalid. This exemplifies the core issue addressed by the clang error message: the suspension of a coroutine, indicated by co_await, within a catch block, violates the specified constraints of C++ coroutines.

A coroutine function coro is defined with the intention of suspending on an instance of B. However, the critical aspect of the demonstration occurs within the main function. Within a try block, the coro function is invoked, and its execution is attempted to be prematurely terminated using .destroy(). The catch block catches any exception of type A and then incorrectly attempts to suspend the coroutine with co_await C(), directly leading to the compiler error. This operation exemplifies an invalid suspension point according to the C++ coroutine specification, triggering the described error message. The inclusion of this example in the documentation serves to clarify the types of code structures that will result in this specific compiler error, thereby guiding developers towards the correct implementation of coroutines in compliance with the standard.

 
AI Generated


Flags -std=c++20 -xc++

[Try out in Compiler Explorer]

Source
#include <coroutine>

struct A{}; // Empty class to be used in catch

// Awaitable object
struct awaiter {
  bool await_ready() noexcept { return false; } // Always await
  void await_suspend(std::coroutine_handle<>) noexcept {} // Suspend logic
  void await_resume() noexcept {} // Resume logic
};

// B has operator co_await
struct B {
  auto operator co_await() const noexcept { return awaiter{}; }
};

// C has operator co_await and a destructor, making it unable to be used in a catch
struct C {
  C() noexcept {}
  ~C() {}
  auto operator co_await() const noexcept { return awaiter{}; }
};

// Coroutine that co_awaits on B
auto coro() -> std::coroutine_handle<> {
  co_await B();
  co_return;
}

// Main function trying to co_await in a catch block, which is invalid
int main() {
  try {
    coro().destroy();
  } catch (A&) {
    co_await C(); // Invalid use of co_await in catch
  }
}
Compiler Output
<source>:25:6: error: this function cannot be a coroutine: 'std::coroutine_traits<std::coroutine_handle<void>>' has no member named 'promise_type'
<source>:25:6: error: this function cannot be a coroutine: 'std::coroutine_traits<std::coroutine_handle<void>>' has no member named 'promise_type'
<source>:35:5: error: 'co_await' cannot be used in the handler of a try block


Clang Internals (17.0.6)

Git Commit Message

[coroutines][PR40978] Emit error for co_yield within catch block

Summary:
As reported in https://bugs.llvm.org/show_bug.cgi?id=40978, it's an
error to use the `co_yield` or `co_await` keywords outside of a valid
"suspension context" as defined by [expr.await]p2 of
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/n4775.pdf.

Whether or not the current scope was in a function-try-block's
(https://en.cppreference.com/w/cpp/language/function-try-block) handler
could be determined using scope flag `Scope::FnTryCatchScope`. No
such flag existed for a simple C++ catch statement, so this commit adds
one.

Reviewers: GorNishanov, tks2103, rsmith

Reviewed By: GorNishanov

Subscribers: EricWF, jdoerfert, cfe-commits, lewissbaker

Tags: #clang

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

llvm-svn: 356296

Used in Clang Sources

This section lists all occurrences of the diagnostic within the Clang's codebase. For each occurrence, an auto-extracted snipped from the source code is listed including key elements like control structures, functions, or classes. It should illustrate the conditions under which the diagnostic is activated.

clang/lib/Sema/SemaCoroutine.cpp (line 781)

// [expr.await]p2, emphasis added: "An await-expression shall appear only in
// a *potentially evaluated* expression within the compound-statement of a
// function-body *outside of a handler* [...] A context within a function
// where an await-expression can appear is called a suspension context of the
// function."
static bool checkSuspensionContext(Sema &S, SourceLocation Loc, StringRef Keyword) {
  // ...
  // Second emphasis of [expr.await]p2: must be outside of an exception handler.
  if (isWithinCatchScope(S.getCurScope())) {
    S.Diag(Loc, diag::err_coroutine_within_handler) << Keyword;

Triggered in Clang Tests

This section lists all internal Clang test cases that trigger the diagnostic.

clang/test/SemaCXX/coroutines.cpp

  • clang/test/SemaCXX/coroutines.cpp:357:5: error: 'co_await' cannot be used in the handler of a try block
  • clang/test/SemaCXX/coroutines.cpp:365:7: error: 'co_await' cannot be used in the handler of a try block
  • clang/test/SemaCXX/coroutines.cpp:383:5: error: 'co_yield' cannot be used in the handler of a try block