This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of WP status.

### 3460. Unimplementable `noop_coroutine_handle` guarantees

Section: 17.12.5.2.3 [coroutine.handle.noop.resumption] Status: WP Submitter: Casey Carter Opened: 2020-07-01 Last modified: 2020-11-09

Priority: 2

View all issues with WP status.

Discussion:

17.12.5.2.3 [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.

17.12.5.2.5 [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<>`.

[2020-07-12; Reflector prioritization]

Set priority to 2 after reflector discussions.

[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-08-21; Issue processing telecon: moved to Tentatively Ready]

[2020-11-09 Approved In November virtual meeting. Status changed: Tentatively Ready → WP.]

Proposed resolution:

This wording is relative to N4861 and also resolves LWG issue 3469.

1. Edit 17.12.4 [coroutine.handle] as indicated:

   >     namespace std {

   >       […]

   >       template<class Promise>
   >       struct coroutine_handle : coroutine_handle<>
   >       {
   >         // [coroutine.handle.con], construct/reset
   >         using 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-constructed `coroutine_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.

2. Add the following subclause under 17.12.4 [coroutine.handle], immediately after 17.12.4.2 [coroutine.handle.con]:

   > ?.?.?.? Conversion [coroutine.handle.conv]

   >         constexpr operator coroutine_handle<>() const noexcept;

   > > -1- Effects: Equivalent to: `return coroutine_handle<>::from_address(address());`.

3. Edit 17.12.4.4 [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`.

4. Edit 17.12.5.2 [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
   >       };
   >     }

5. Add the following subclause under 17.12.5.2 [coroutine.handle.noop], immediately before 17.12.5.2.2 [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());`.