Skip to content

Say so when a sender adaptor closure is used as a sender - #2378

Open
veljjanoski wants to merge 1 commit into
NVIDIA:mainfrom
veljjanoski:improve-closure-diagnostic
Open

veljjanoski wants to merge 1 commit into
NVIDIA:mainfrom
veljjanoski:improve-closure-diagnostic

Conversation

@veljjanoski

Copy link
Copy Markdown
Contributor

Part of #709.

What

Passing a sender adaptor closure where a sender is expected, for example

auto closure = stdexec::then([] { return 1; });
stdexec::sync_wait(closure);

gives the generic message that the type is not a sender because enable_sender is false, with advice to add a sender_concept or specialize enable_sender. That advice does not help here. This PR adds a branch for closures to __diagnose_sender_concept_failure. With Clang 21:

error: static assertion failed due to requirement 'enable_sender<stdexec::__closure<stdexec::then_t, (lambda at q.cpp:3:36)>>':

The given type is a sender adaptor closure, not a sender. A closure such as
'stdexec::then(fn)' needs a sender to adapt.

Either pipe a sender into it:

     auto sndr2 = sndr | stdexec::then(fn);

or pass the sender as the first argument:

     auto sndr2 = stdexec::then(sndr, fn);

It also adds the missing space in the existing message (enable_sender<Sender>is false).

The check is a small concept in __detail, not the existing __sender_adaptor_closure. That header includes __sender_concepts.hpp (through __completion_signatures_of.hpp), so it cannot be included here. A concept is also needed because sender_adaptor_closure<T> only accepts class types: with a plain if constexpr, sync_wait(42) would become a hard error. The branch only runs when enable_sender is false, so a type that is both a closure and a sender is not affected.

Not covered, hence "part of":

  • The adaptor object itself, for example sync_wait(stdexec::then). There is no common trait for adaptor objects, so it still gets the generic message.
  • A closure passed to an algorithm that takes a sender, for example stdexec::then(closure, f) or spawn(closure, token). These fail the algorithm's own sender constraint before this diagnostic runs.

Verification

Docker (Debian 12), GCC 12.2 and Clang 21.1 with libstdc++ 12. The message for sync_wait(x):

x main this PR
a closure: lvalue, prvalue, const, composed then(f) | then(g) generic enable_sender message closure message
a user type derived from sender_adaptor_closure generic closure message
42, stdexec::then generic generic, with the space fixed

Valid uses still build and run: just(1) | closure, and a sequence through exec::iterate | exec::ignore_all_values(). test.stdexec and test.exec (Debug) build and pass with both compilers: 644 and 454 test cases with Clang, 643 and 454 with GCC. clang-format 21 is clean.

There is no new test, because the repo has no compile-fail test support yet (#1260).

AI-usage disclosure: Claude (Anthropic) was used as assistant.

@copy-pr-bot

copy-pr-bot Bot commented Oct 28, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant