From 2f0dba08e3284d40c3655c2e7364d6ca4fb1fbf1 Mon Sep 17 00:00:00 2001 From: guptapratykshh Date: Wed, 20 May 2026 08:56:01 +0530 Subject: [PATCH] make_future: replace static_assert in detail::make_future with HPX_ASSERT_MSG to preserve runtime guard without breaking GCC 12 debug SFINAE Signed-off-by: guptapratykshh --- .../hpx/execution/algorithms/make_future.hpp | 36 ++++++++++++------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/libs/core/execution/include/hpx/execution/algorithms/make_future.hpp b/libs/core/execution/include/hpx/execution/algorithms/make_future.hpp index 9b834c4a5e1..debc23775d7 100644 --- a/libs/core/execution/include/hpx/execution/algorithms/make_future.hpp +++ b/libs/core/execution/include/hpx/execution/algorithms/make_future.hpp @@ -7,6 +7,7 @@ #pragma once +#include #include #include #include @@ -218,11 +219,10 @@ namespace hpx::execution::experimental { }; // Detects whether a sender's set_value completion scheduler is - // run_loop_scheduler. Used by detail::make_future to reject -- at - // compile time -- the 1-argument form make_future(sender) when the - // sender's completion path runs on a run_loop scheduler. That call - // would silently hang at runtime because the 1-argument overload - // constructs a future_data that never drives loop.run(). + // run_loop_scheduler. Used by detail::make_future as a runtime + // guard (see HPX_ASSERT_MSG below). The trait itself is SFINAE- + // friendly so it can be safely instantiated during overload + // resolution. template struct sender_completion_is_run_loop_scheduler : std::false_type { @@ -247,13 +247,25 @@ namespace hpx::execution::experimental { HPX_CXX_CORE_EXPORT template auto make_future(Sender&& sender, Allocator const& allocator) { - // The 1-argument make_future(sender) overload constructs a - // future_data that never calls loop.run(), so passing a sender - // whose set_value completion scheduler is a run_loop_scheduler - // produces a future that silently hangs. Reject at compile time - // and direct the user to make_future(sched, sender). - static_assert(!sender_completion_is_run_loop_scheduler< - std::decay_t>::value, + // Debug-only runtime guard against the silent-hang case: + // this 1-argument fallback constructs a future_data that + // never drives loop.run(), so a sender whose set_value + // completion scheduler is a run_loop_scheduler produces a + // future that hangs in get(). The public make_future CPO + // routes such senders to the scheduler-form overload via + // tag_override_invoke, so reaching this body with a run-loop + // completion scheduler indicates a direct call to + // detail::make_future rather than the public API. HPX_ASSERT_MSG + // is a no-op in release builds (HPX_DEBUG off), so this only + // catches the misuse in debug; an unconditional throw would + // also alter the release-mode failure shape and is left out + // intentionally -- the assert exists to flag bypassing the + // public CPO during development, not to harden release-mode + // misuse. static_assert is avoided here because tag_priority's + // SFINAE probing during overload resolution may instantiate + // this body before the scheduler-form overload is selected. + HPX_ASSERT_MSG(!sender_completion_is_run_loop_scheduler< + std::decay_t>::value, "make_future(sender) cannot drive the parent run_loop when " "the sender's completion scheduler is a run_loop_scheduler. " "Use make_future(loop.get_scheduler(), sender) so the "