From 19d420477c0ac59c1cfceef6bc0d937e542985f5 Mon Sep 17 00:00:00 2001 From: Eric Niebler Date: Fri, 5 Sep 2025 09:57:28 -0700 Subject: [PATCH] make the try/catch portability macros safer --- .clang-format | 3 +- .clangd | 1 + examples/hello_coro.cpp | 7 +++-- .../__system_context_default_impl.hpp | 6 ++-- include/stdexec/__detail/__config.hpp | 30 ++++++++++--------- test/exec/async_scope/test_spawn_future.cpp | 3 ++ test/exec/test_libdispatch.cpp | 3 ++ test/rrd/async_scope.cpp | 3 ++ .../stdexec/algos/adaptors/test_let_error.cpp | 1 + test/test_common/receivers.hpp | 3 ++ 10 files changed, 40 insertions(+), 20 deletions(-) diff --git a/.clang-format b/.clang-format index 8c529e0cb..0d22ea7d3 100644 --- a/.clang-format +++ b/.clang-format @@ -72,7 +72,7 @@ EmptyLineBeforeAccessModifier: Leave ExperimentalAutoDetectBinPacking: true FixNamespaceComments: true IfMacros: [ - STDEXEC_CATCH' + 'STDEXEC_CATCH' ] IncludeBlocks: Preserve IndentAccessModifiers: false @@ -88,6 +88,7 @@ KeepEmptyLinesAtTheStartOfBlocks: true LambdaBodyIndentation: Signature LineEnding: LF Macros: [ + 'STDEXEC_CATCH_FALLTHROUGH= ', 'STDEXEC_MEMFN_DECL(...)=__VA_ARGS__', 'STDEXEC_ATTRIBUTE(...)=__attribute__((__VA_ARGS__))', 'STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS=[[no_unique_address]]', diff --git a/.clangd b/.clangd index ea3b3fd5c..1ce0ad71b 100644 --- a/.clangd +++ b/.clangd @@ -53,6 +53,7 @@ CompileFlags: - "-ftemplate-backtrace-limit=0" - "-std=gnu++20" - "-DSTDEXEC_CLANGD_INVOKED" + - "-stdlib=libc++" Remove: - "-stdpar*" # strip CUDA fatbin args diff --git a/examples/hello_coro.cpp b/examples/hello_coro.cpp index e9f9d4a3b..ae723c389 100644 --- a/examples/hello_coro.cpp +++ b/examples/hello_coro.cpp @@ -46,8 +46,11 @@ auto main() -> int { auto [i] = stdexec::sync_wait(async_answer2(just(42), just())).value(); std::cout << "The answer is " << i.value() << '\n'; } - STDEXEC_CATCH(const std::exception& e) { - std::cout << e.what() << '\n'; + STDEXEC_CATCH(std::exception& e) { + std::cerr << "error: " << e.what() << '\n'; + } + STDEXEC_CATCH_ALL { + std::cerr << "unknown error\n"; } } #else diff --git a/include/exec/__detail/__system_context_default_impl.hpp b/include/exec/__detail/__system_context_default_impl.hpp index 3552830f4..a8f5578bb 100644 --- a/include/exec/__detail/__system_context_default_impl.hpp +++ b/include/exec/__detail/__system_context_default_impl.hpp @@ -249,7 +249,7 @@ namespace exec::__system_context_default_impl { __schedule_operation_t::__construct_maybe_alloc(__storage, &__r, std::move(__sndr)); __os->start(); } - STDEXEC_CATCH(std::exception & __e) { + STDEXEC_CATCH_ALL { __r.set_error(std::current_exception()); } } @@ -278,7 +278,7 @@ namespace exec::__system_context_default_impl { __storage, &__r, std::move(__sndr)); __os->start(); } - STDEXEC_CATCH(std::exception & __e) { + STDEXEC_CATCH_ALL { __r.set_error(std::current_exception()); } } @@ -297,7 +297,7 @@ namespace exec::__system_context_default_impl { __storage, &__r, std::move(__sndr)); __os->start(); } - STDEXEC_CATCH(std::exception & __e) { + STDEXEC_CATCH_ALL { __r.set_error(std::current_exception()); } } diff --git a/include/stdexec/__detail/__config.hpp b/include/stdexec/__detail/__config.hpp index 810bac734..6fc7c3a4a 100644 --- a/include/stdexec/__detail/__config.hpp +++ b/include/stdexec/__detail/__config.hpp @@ -540,6 +540,8 @@ namespace stdexec { # include #endif +// clang-format off + // The following macros are used to conditionally compile exception handling code. They // are used in the same way as `try` and `catch`, but they allow for different behavior // based on whether exceptions are enabled or not, and whether the code is being compiled @@ -559,28 +561,28 @@ namespace stdexec { // printf("unknown error\n"); // } #if STDEXEC_STD_NO_EXCEPTIONS() -# define STDEXEC_TRY if constexpr (true) -# define STDEXEC_CATCH(...) \ - else if constexpr ([[maybe_unused]] __VA_ARGS__ = ::stdexec::_catch_any_lvalue{}; false) -# define STDEXEC_CATCH_ALL \ - else if constexpr (true) { \ - } \ - else -# define STDEXEC_THROW(...) ::stdexec::__terminate() +# define STDEXEC_TRY if constexpr (true) { +# define STDEXEC_CATCH(...) } else if constexpr (__VA_ARGS__ = ::stdexec::__catch_any_lvalue; false) { +# define STDEXEC_CATCH_ALL } else if constexpr (true) {} else +# define STDEXEC_THROW(...) ::stdexec::__terminate() +# define STDEXEC_CATCH_FALLTHROUGH } else {} #else -# define STDEXEC_TRY try -# define STDEXEC_CATCH catch -# define STDEXEC_CATCH_ALL STDEXEC_CATCH(...) -# define STDEXEC_THROW(...) throw __VA_ARGS__ +# define STDEXEC_TRY try +# define STDEXEC_CATCH catch +# define STDEXEC_CATCH_ALL catch(...) +# define STDEXEC_THROW(...) throw __VA_ARGS__ +# define STDEXEC_CATCH_FALLTHROUGH #endif +// clang-format on + namespace stdexec { // Used by the STDEXEC_CATCH macro to provide a stub initialization of the exception object. - struct _catch_any_lvalue { + constexpr struct __catch_any_lvalue_t { template STDEXEC_ATTRIBUTE(host, device) operator _Tp&() const noexcept; - }; + } __catch_any_lvalue{}; STDEXEC_ATTRIBUTE(noreturn, host, device) inline void __terminate() noexcept { diff --git a/test/exec/async_scope/test_spawn_future.cpp b/test/exec/async_scope/test_spawn_future.cpp index fdf458785..8d592e979 100644 --- a/test/exec/async_scope/test_spawn_future.cpp +++ b/test/exec/async_scope/test_spawn_future.cpp @@ -165,6 +165,9 @@ namespace { STDEXEC_CATCH(const std::logic_error& e) { SUCCEED("correct exception caught"); } + STDEXEC_CATCH_ALL { + FAIL("invalid exception caught"); + } sync_wait(scope.on_empty()); } #endif // !STDEXEC_STD_NO_EXCEPTIONS() diff --git a/test/exec/test_libdispatch.cpp b/test/exec/test_libdispatch.cpp index f1544ab46..1aec9a5d9 100644 --- a/test/exec/test_libdispatch.cpp +++ b/test/exec/test_libdispatch.cpp @@ -86,5 +86,8 @@ namespace { STDEXEC_CATCH(int e) { CHECK(e == 999); } + STDEXEC_CATCH_ALL { + FAIL("invalid exception caught"); + } } } // namespace diff --git a/test/rrd/async_scope.cpp b/test/rrd/async_scope.cpp index d8f1964f2..4fd281f33 100644 --- a/test/rrd/async_scope.cpp +++ b/test/rrd/async_scope.cpp @@ -75,6 +75,9 @@ struct async_scope_future_set_result : rl::test_suite