C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Allowing coroutine_handle::from_promise to accept a pointer-interconvertible object

From: Aaron Jacobs <jacobsa_at_[hidden]>
Date: Wed, 4 Jan 2023 10:33:11 +1100
On Wed, Jan 4, 2023 at 2:18 AM Arthur O'Dwyer <arthur.j.odwyer_at_[hidden]> wrote:
> Just looking at this proposed wording, it doesn't seem like your
> proposal does anything. A pointer that points to "an object
> pointer-interconvertible with X, with the same alignment as X" is
> exactly the same, machine-wise, as a pointer that points to "an
> X" — all you have to do is cast it to (X*) before passing it to
> `from_promise`.

I agree with Jason: while you are totally right in a practical sense (and that
is why there is no compatibility issue with existing implementations), the
standard says this is not allowed, because a reference to a base class
sub-object of a coroutine promise is not a reference to a coroutine promise,
even if they happen to have the same address This is also what @timsong-cpp
said in https://github.com/cplusplus/draft/issues/6039.

This is exactly why I want to change the precondition: it forbids something
that is completely practical, where the machine can't tell the difference in
real implementations. The precondition seems to just be tighter than necessary.

(Why do I care at all if the machine can't tell the difference? Well, relying
on UB is bad, and you can imagine in the future some sanitizer or other runtime
check causes this assumption to break in some way. Perhaps it stores
information about the type in the coroutine handle, not just the address.)


> Your code snippets don't actually involve any calls to
> `from_promise`. Maybe it would be clearer if you showed a Tony Table,
> like "here's something that doesn't compile / doesn't work today"
> versus "here's how it would work after this proposal."

Sorry, I thought it would be clear from context. I'm just looking for a UB-free
way to create a `std::coroutine_handle<PromiseBase>` from a `Promise&`;
something like the following:

    // Called when my coroutine co_awaits the result of another
    // coroutine whose promise also inherits from
    // CoroutinePromiseBase in a pointer-interconvertible way.
    template <typename MyResult>
    template <typename TheirResult>
    void Promise<MyResult>::WaitFor(Promise<TheirResult>& child) {
      [...]

      // Tell the child coroutine to resume the parent coroutine
      // after it finishes.
      child.waiter =
          std::coroutine_handle<PromiseBase>::from_promise(*this);

      [...]
    }

As both you and I said above, this actually works fine in a practical sense
today: it compiles, and causes no runtime issues with the real implementation
in clang (and I expect gcc and MSVC too). But I think it is technically
undefined behavior.

Aaron

Received on 2023-01-03 23:33:39