Skip to content

Conversation

@RobertLeahy
Copy link
Contributor

Please do not squash.

The type returned by stdexec::__apply was previously specified via
decltype(auto). This is convenient to write, but means that in order to
determine the return type the compiler must substitute into the actual
body of the function. Doing this to constexpr functions causes Clang (at
least up to 21.1.0) to fail to build the get_env function of many
receiver types which are member types of an operation state (and which
contain a reference back to that operation state). Consider this example
which doesn't build on Clang 21.1.0 and which is intended to be similar
to the aforementioned situation:

  template<typename T>
  constexpr auto get(T& t) noexcept {
    return t.get();
  }

  template<typename T>
  struct state {
    using return_type = typename T::type;
    struct inner {
      constexpr return_type get() const noexcept;
      state& self;
    };
    static_assert(
      std::is_same_v<
        decltype(get(std::declval<inner&>())),
        int>);
    T t;
  };

  template<typename T>
  constexpr auto state<T>::inner::get() const noexcept -> return_type {
    return self.t.get();
  }

  struct t {
    using type = int;
    constexpr int get() const noexcept {
      return i;
    }
    int i;
  };

  constexpr auto impl() noexcept {
    state<t> s{t{5}};
    state<t>::inner i{s};
    return get(i);
  }

The error given by clang is that state<t> is incomplete when it's used
within state<t>::inner::get. The backtrace associated with the
compilation error identifies the static_assert as the problematic source
of the use, if it is removed then Clang 21.1.0 accepts the above. This
is eyebrow-raising for at least two reasons:

- The point of use of state<t> is within the out of line definition of
  state::inner::get which occurs lexically after state is complete
- state::inner::get does not have a deduced return type, therefore the
  compiler has all the information necessary to determine the return
  type of get (the free function) without considering the definition of
  state::inner::get

Codifying the second bullet by explicitly specifying the return type of
get (the free function):

  constexpr auto get(T& t) noexcept -> decltype(t.get()) {
    return t.get();
  }

Causes Clang 21.1.0 to accept the above example (because it doesn't
attempt to build the body of state::inner::get from a context whereat
state is incomplete).

Note that removing constexpr from state::inner::get causes Clang 21.1.0
to accept the original example.

This discussion may seem to have nothing to do with stdexec::__apply
until one examines the backtrace generated when Clang fails to build the
get_env member function of affected receiver types: The backtrace
radiates from building the body of stdexec::__apply.

Explicitly specified the return type of stdexec::__apply to ameliorate
the above.
@copy-pr-bot
Copy link

copy-pr-bot bot commented Jan 25, 2026

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.

@ericniebler
Copy link
Collaborator

/ok to test 52deced

@ericniebler
Copy link
Collaborator

/ok to test 2beb079

romintomasetti added a commit to romintomasetti/stdexec that referenced this pull request Jan 26, 2026
Signed-off-by: romintomasetti <romin.tomasetti@gmail.com>
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.

2 participants