Created on 2020-07-01.00:00:00 last changed 13 months ago
Proposed resolution:
This wording is relative to N4861 and also resolves LWG issue 3469.
Edit [coroutine.handle] as indicated:
namespace std { […] template<class Promise> struct coroutine_handle: coroutine_handle<>{ // [coroutine.handle.con], construct/resetusing coroutine_handle<>::coroutine_handle;constexpr coroutine_handle() noexcept; constexpr coroutine_handle(nullptr_t) noexcept; static coroutine_handle from_promise(Promise&); coroutine_handle& operator=(nullptr_t) noexcept; // [coroutine.handle.export.import], export/import constexpr void* address() const noexcept; static constexpr coroutine_handle from_address(void* addr); // [coroutine.handle.conv], conversion constexpr operator coroutine_handle<>() const noexcept; // [coroutine.handle.observers], observers constexpr explicit operator bool() const noexcept; bool done() const; // [coroutine.handle.resumption], resumption void operator()() const; void resume() const; void destroy() const; // [coroutine.handle.promise], promise access Promise& promise() const; private: void* ptr; // exposition only }; }-1- An object of type coroutine_handle<T> is called a coroutine handle and can be used to refer to a suspended or executing coroutine. A
default-constructedcoroutine_handle object whose member address() returns a null pointer value does not refer to any coroutine. Two coroutine_handle objects refer to the same coroutine if and only if their member address() returns the same value.
Add the following subclause under [coroutine.handle], immediately after [coroutine.handle.con]:
?.?.?.? Conversion [coroutine.handle.conv]
constexpr operator coroutine_handle<>() const noexcept;-1- Effects: Equivalent to: return coroutine_handle<>::from_address(address());.
Edit [coroutine.handle.export.import] as indicated, splitting the two versions:
static constexpr coroutine_handle<> coroutine_handle<>::from_address(void* addr);-?- Preconditions: addr was obtained via a prior call to address on an object whose type is a specialization of coroutine_handle.
-?- Postconditions: from_address(address()) == *this.static constexpr coroutine_handle<Promise> coroutine_handle<Promise>::from_address(void* addr);-2- Preconditions: addr was obtained via a prior call to address on an object of type cv coroutine_handle<Promise>.
-3- Postconditions: from_address(address()) == *this.
Edit [coroutine.handle.noop] as indicated:
namespace std { template<> struct coroutine_handle<noop_coroutine_promise>: coroutine_handle<>{ // [coroutine.handle.noop.conv], conversion constexpr operator coroutine_handle<>() const noexcept; // [coroutine.handle.noop.observers], observers constexpr explicit operator bool() const noexcept; constexpr bool done() const noexcept; // [coroutine.handle.noop.resumption], resumption constexpr void operator()() const noexcept; constexpr void resume() const noexcept; constexpr void destroy() const noexcept; // [coroutine.handle.noop.promise], promise access noop_coroutine_promise& promise() const noexcept; // [coroutine.handle.noop.address], address constexpr void* address() const noexcept; private: coroutine_handle(unspecified); void* ptr; // exposition only }; }
Add the following subclause under [coroutine.handle.noop], immediately before [coroutine.handle.noop.observers]:
?.?.?.?.? Conversion [coroutine.handle.noop.conv]
constexpr operator coroutine_handle<>() const noexcept;-1- Effects: Equivalent to: return coroutine_handle<>::from_address(address());.
[ 2020-11-09 Approved In November virtual meeting. Status changed: Tentatively Ready → WP. ]
[ 2020-08-21; Issue processing telecon: moved to Tentatively Ready ]
[ 2020-07-29 Tim adds PR and comments ]
The root cause for this issue as well as issue 3469 is the unnecessary public derivation from coroutine_handle<void>. The proposed resolution below replaces the derivation with a conversion function and adds explicit declarations for members that were previously inherited. It also modifies the preconditions on from_address with goal of making it impossible to obtain a coroutine_handle<P> to a coroutine whose promise type is not P in well-defined code.
[ 2020-07-12; Reflector prioritization ]
Set priority to 2 after reflector discussions.
[coroutine.handle.noop.resumption]/2 states "Remarks: If noop_coroutine_handle is converted to coroutine_handle<>, calls to operator(), resume and destroy on that handle will also have no observable effects." This suggests that e.g. in this function:
void f(coroutine_handle<> meow) { auto woof = noop_coroutine(); static_cast<coroutine_handle<>&>(woof) = meow; static_cast<coroutine_handle<>&>(woof).resume(); }
the final call to coroutine_handle<>::resume must have no effect regardless of what coroutine (if any) meow refers to, contradicting the specification of coroutine_handle<>::resume. Even absent this contradiction, implementing the specification requires coroutine_handle<>::resume to determine if *this is a base subobject of a noop_coroutine_handle, which seems pointlessly expensive to implement.
[coroutine.handle.noop.address]/2 states "Remarks: A noop_coroutine_handle's ptr is always a non-null pointer value." Similar to the above case, a slicing assignment of a default-initialized coroutine_handle<> to a noop_coroutine_handle must result in ptr having a null pointer value — another contradiction between the requirements of noop_coroutine_handle and coroutine_handle<>.History | |||
---|---|---|---|
Date | User | Action | Args |
2023-11-22 15:47:43 | admin | set | status: wp -> c++23 |
2020-11-09 21:40:50 | admin | set | messages: + msg11581 |
2020-11-09 21:40:50 | admin | set | status: ready -> wp |
2020-08-21 20:18:09 | admin | set | messages: + msg11445 |
2020-08-21 20:18:09 | admin | set | status: new -> ready |
2020-07-30 02:39:26 | admin | set | messages: + msg11420 |
2020-07-30 02:39:26 | admin | set | messages: + msg11419 |
2020-07-12 16:33:52 | admin | set | messages: + msg11364 |
2020-07-01 00:00:00 | admin | create |