Commit 8ee3a91
stdexec::__apply: Do Not Deduce Return Type
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.1 parent d0096bc commit 8ee3a91
1 file changed
+6
-4
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
219 | 219 | | |
220 | 220 | | |
221 | 221 | | |
222 | | - | |
223 | | - | |
224 | | - | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
225 | 226 | | |
226 | 227 | | |
227 | 228 | | |
| |||
232 | 233 | | |
233 | 234 | | |
234 | 235 | | |
235 | | - | |
| 236 | + | |
| 237 | + | |
236 | 238 | | |
237 | 239 | | |
238 | 240 | | |
| |||
0 commit comments