On 1/9/26 6:01 PM, Iain Sandoe wrote:


On 9 Jan 2026, at 08:37, Nathaniel Shead <[email protected]> wrote:

On Fri, Jan 09, 2026 at 08:19:33AM +0000, Iain Sandoe wrote:
Hi Nathaniel.

thanks for looking at this (both language features were in flux at the same
time and I don’t think the interaction was especially well-considered).

On 9 Jan 2026, at 04:39, Nathaniel Shead <[email protected]> wrote:

Bootstrapped and regtested on x86_64-pc-linux-gnu, OK for trunk?

-- >8 --

While working on another issue I found that currently modules do not
work with coroutines at all.  This patch fixes a number of issues in
both the coroutines logic and modules logic to ensure that they play
well together.  To summarize:

- The coroutine proxy objects did not have a DECL_CONTEXT set (required
for modules to merge declarations).

- The coroutine transformation functions were always considered
non-inline, even for an inline ramp function, which meant that modules
didn't realise it needed to stream a definition.

I am somewhat concerned about the proposed change here (it looks like
an ABI change - albeit an addition so, presumably, not breaking).

The principle is that the three functions are all considered to be part of the
same entity, where the user-visible interface is only the ramp and the coroutine
handle.

That is, I don’t think that this is an ‘exposure’ in the sense of P1815 since
the split into ramp/actor/destroyer is an internal detail invisible to the end
user.


I think perhaps my wording wasn't clear; this wasn't so much about
internal linkage (though that of course is also related) but about vague
linkage.  Consider the following TU:

  #include <coroutine>
  struct simple_promise;
  struct simple_coroutine : std::coroutine_handle<simple_promise> {
    using promise_type = ::simple_promise;
  };
  struct simple_promise {
    simple_coroutine get_return_object() { return { 
simple_coroutine::from_promise(*this) }; }
    std::suspend_always initial_suspend() noexcept { return {}; }
    std::suspend_always final_suspend() noexcept { return {}; }
    void return_void() {}
    void unhandled_exception() {}
  };
  inline simple_coroutine foo() {
    co_return;
  }

We do not emit foo as it is unused, but we do emit the transform
functions (e.g. _Z3fooP13_Z3foov.Frame.actor).

I think that is https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102528

These functions are
also not marked as '.globl' (they are not TREE_PUBLIC and so are
considered internal to the TU), so a different TU calling a
forward-declared 'foo' would get undefined references to the actor
function etc.

I’m not sure what you mean by ‘forward-declared’ here; for foo() to be
usable there must be TU(s) in which foo() is emitted - that/those TU(s)
need to contain the helpers.

My assumption was that the intention is that they are all meant to come
along for the ride with each other wrt linkage; that is, in this case
foo has vague linkage, so foo.actor and foo.destroy should be as well,
and will be emitted iff foo is.  Rather than every TU having copies of
the transform functions.

I agree that this seems more space-efficient (at the expense of emitting more
vague-linkage symbols).  I think it would, however break the assumptions
made if, for example, foo () in one TU was able to link against the helpers in
another - since the details of the lowering are compiler-specific.

We can avoid that by putting them all in the same comdat group.

Jason

Reply via email to