public inbox for gcc-bugs@sourceware.org
help / color / mirror / Atom feed
* [Bug c++/95540] New: [coroutine] coroutine_traits<> lookup for lambdas
@ 2020-06-04 17:03 bruck.michael at gmail dot com
  2020-06-04 17:24 ` [Bug c++/95540] " bruck.michael at gmail dot com
                   ` (7 more replies)
  0 siblings, 8 replies; 9+ messages in thread
From: bruck.michael at gmail dot com @ 2020-06-04 17:03 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95540

            Bug ID: 95540
           Summary: [coroutine] coroutine_traits<> lookup for lambdas
           Product: gcc
           Version: 10.1.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: bruck.michael at gmail dot com
  Target Milestone: ---

This fails on gcc and clang

gcc: unable to find the promise type for this coroutine
clang: ...coroutine_traits<.., const (lambda at <source>:30:12) &>' has no
member named 'promise_type'

https://gcc.godbolt.org/z/kXzEZW

The clang error hints that the lookup is performed with P1 being the lambda
closure type. I suspect gcc does the same (there is potential for a more
detailed diagnostic here). This makes it impossible to use coroutine_traits<>
with lambdas.

n4835:
[dcl.fct.def.coroutine]
"The promise type of a coroutine is std::coroutine_traits<R, P1, . . . ,
Pn>::promise_type, where R is the return type of the function, and P1 . . . Pn
are the sequence of types of the function parameters, preceded by the type of
the implicit object parameter (12.4.1) if the coroutine is a non-static member
function. The promise type shall be a class type."

While a lambda does have a non-static member operator() the lambda expression
creates a closure object, not a member function. In any case either the
standard or the interpretation of the compilers prevents the use of
coroutine_traits<> for lambdas without a discernible benefit.


#ifndef __clang__
#include <coroutine>
#else
#include <experimental/coroutine>
namespace std { using namespace experimental; }
#endif

#include <cstdio>

struct pt
{
    using handle_t = std::coroutine_handle<pt>;
    auto get_return_object() noexcept { return handle_t::from_promise(*this); }

    std::suspend_never initial_suspend() const noexcept { return {}; }
    std::suspend_never final_suspend() const noexcept { return {}; }
    void return_void() const noexcept {}
    void unhandled_exception() const noexcept {}
};

template <> struct std::coroutine_traits<pt::handle_t>
    { using promise_type = pt; };

static pt::handle_t foo()
{ 
    printf("from function\n");
    co_return;
}

auto bar = []() -> pt::handle_t
{ 
    printf("from lambda\n");
    co_return;
};

int main()
{
    foo();
    bar();
}

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [Bug c++/95540] [coroutine] coroutine_traits<> lookup for lambdas
  2020-06-04 17:03 [Bug c++/95540] New: [coroutine] coroutine_traits<> lookup for lambdas bruck.michael at gmail dot com
@ 2020-06-04 17:24 ` bruck.michael at gmail dot com
  2020-06-05  7:32 ` iains at gcc dot gnu.org
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: bruck.michael at gmail dot com @ 2020-06-04 17:24 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95540

--- Comment #1 from Michael Bruck <bruck.michael at gmail dot com> ---
"impossible" is too strong here, you can add another overload:

template <typename T> struct std::coroutine_traits<pt::handle_t, T>
    { using promise_type = pt; };

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [Bug c++/95540] [coroutine] coroutine_traits<> lookup for lambdas
  2020-06-04 17:03 [Bug c++/95540] New: [coroutine] coroutine_traits<> lookup for lambdas bruck.michael at gmail dot com
  2020-06-04 17:24 ` [Bug c++/95540] " bruck.michael at gmail dot com
@ 2020-06-05  7:32 ` iains at gcc dot gnu.org
  2020-06-05 15:38 ` bruck.michael at gmail dot com
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: iains at gcc dot gnu.org @ 2020-06-05  7:32 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95540

Iain Sandoe <iains at gcc dot gnu.org> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|UNCONFIRMED                 |NEW
     Ever confirmed|0                           |1
           Assignee|unassigned at gcc dot gnu.org      |iains at gcc dot gnu.org
   Target Milestone|---                         |10.2
   Last reconfirmed|                            |2020-06-05

--- Comment #2 from Iain Sandoe <iains at gcc dot gnu.org> ---
There was a long discussion amongst the implementors at WG21 meeting in Prague
about what should be done for the closure object.  The standard is really
silent about coroutines and lambdas except by inference (and different
implementors had made different inferences).

The end result of the  discussion is that we should treat it the same way as a
class object (passing a reference to it to the traits, allocator lookup and
promise parameter preview).

current MSVC is the only implementation to have this complete, clang passes the
reference to the traits but not to the allocator / promise. GCC (current code)
passes the closure pointer  to all three.

I posted a patch here:
https://gcc.gnu.org/pipermail/gcc-patches/2020-May/546299.html
to bring GCC up to date with the agreed interpretation.

After that, diagnostics will (as ever) be a QoI issue - as far as coroutines
go, the implementation makes use of the same lookup facilities as the rest of
the C++ FE (so we should expect a consistent behaviour in terms of
diagnostics).

(libraries like cppcoro etc. make heavy use of coroutine lambdas, so they are
certainly found to be viable).

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [Bug c++/95540] [coroutine] coroutine_traits<> lookup for lambdas
  2020-06-04 17:03 [Bug c++/95540] New: [coroutine] coroutine_traits<> lookup for lambdas bruck.michael at gmail dot com
  2020-06-04 17:24 ` [Bug c++/95540] " bruck.michael at gmail dot com
  2020-06-05  7:32 ` iains at gcc dot gnu.org
@ 2020-06-05 15:38 ` bruck.michael at gmail dot com
  2020-06-05 15:54 ` iains at gcc dot gnu.org
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: bruck.michael at gmail dot com @ 2020-06-05 15:38 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95540

--- Comment #3 from Michael Bruck <bruck.michael at gmail dot com> ---
I think you misread, I was complaining about it passing the closure to the
traits/constructor/allocator. But if that is what was agreed upon...

Can the closure object that is being passed to the constructor and allocator be
used for anything? I am asking because I got it to pass nullptr with the code
below. But if there is no legitimate use it might as well always pass a
nullptr.

template <pt::handle_t F()>
struct foo_t
{
    static auto test()
    {
        return F();
    }
};

using moo = foo_t<[]() -> pt::handle_t{ co_return; }>;
...
int main { moo::test(); }

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [Bug c++/95540] [coroutine] coroutine_traits<> lookup for lambdas
  2020-06-04 17:03 [Bug c++/95540] New: [coroutine] coroutine_traits<> lookup for lambdas bruck.michael at gmail dot com
                   ` (2 preceding siblings ...)
  2020-06-05 15:38 ` bruck.michael at gmail dot com
@ 2020-06-05 15:54 ` iains at gcc dot gnu.org
  2020-06-05 18:52 ` bruck.michael at gmail dot com
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: iains at gcc dot gnu.org @ 2020-06-05 15:54 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95540

--- Comment #4 from Iain Sandoe <iains at gcc dot gnu.org> ---
(In reply to Michael Bruck from comment #3)
> I think you misread, I was complaining about it passing the closure to the
> traits/constructor/allocator. But if that is what was agreed upon...
> 
> Can the closure object that is being passed to the constructor and allocator
> be used for anything? I am asking because I got it to pass nullptr with the
> code below. But if there is no legitimate use it might as well always pass a
> nullptr.

OK.. I think I did misread (I was under the impression you wanted better
diagnostics for the case where the user had intentionally specialised the
traits - but made an error). [FTR, I am not sure if that's feasible - but
someone with more specialisation-fu can comment on that].

---

I think we need the closure object type in the traits lookup because:

Since the coroutine traits are global, and the closure type is unique, that
provides a way to disambiguate instantiations of the traits for lambdas with
otherwise identical signatures.

The callable is usable in the other positions - I don't think that was
particularly in debate.

The issue was about whether we should pass a reference to the closure or the
pointer (and it was decided that symmetry with the class object that a
reference was the intention).

The fix is not yet in master (to make it a reference) - it's pending review
(but you could apply it to test things out).

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [Bug c++/95540] [coroutine] coroutine_traits<> lookup for lambdas
  2020-06-04 17:03 [Bug c++/95540] New: [coroutine] coroutine_traits<> lookup for lambdas bruck.michael at gmail dot com
                   ` (3 preceding siblings ...)
  2020-06-05 15:54 ` iains at gcc dot gnu.org
@ 2020-06-05 18:52 ` bruck.michael at gmail dot com
  2020-06-05 19:19 ` iains at gcc dot gnu.org
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: bruck.michael at gmail dot com @ 2020-06-05 18:52 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95540

--- Comment #5 from Michael Bruck <bruck.michael at gmail dot com> ---
> Since the coroutine traits are global, and the closure type is unique,
> that provides a way to disambiguate instantiations of the traits for
> lambdas with otherwise identical signatures.

But the closure type is generated in the same lambda expression where the
coroutine generator looks up the coroutine_traits<>. How do I specialize on
that?

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [Bug c++/95540] [coroutine] coroutine_traits<> lookup for lambdas
  2020-06-04 17:03 [Bug c++/95540] New: [coroutine] coroutine_traits<> lookup for lambdas bruck.michael at gmail dot com
                   ` (4 preceding siblings ...)
  2020-06-05 18:52 ` bruck.michael at gmail dot com
@ 2020-06-05 19:19 ` iains at gcc dot gnu.org
  2020-06-11  0:17 ` iains at gcc dot gnu.org
  2020-06-11  7:40 ` bruck.michael at gmail dot com
  7 siblings, 0 replies; 9+ messages in thread
From: iains at gcc dot gnu.org @ 2020-06-05 19:19 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95540

--- Comment #6 from Iain Sandoe <iains at gcc dot gnu.org> ---
(In reply to Michael Bruck from comment #5)
> > Since the coroutine traits are global, and the closure type is unique,
> > that provides a way to disambiguate instantiations of the traits for
> > lambdas with otherwise identical signatures.
> 
> But the closure type is generated in the same lambda expression where the
> coroutine generator looks up the coroutine_traits<>. How do I specialize on
> that?

I wasn't meaning to suggest you can specialise per lambda (but I was saying
that the traits instantiated will be unique per lambda) - perhaps the latter is
an implementation detail.

Providing the class/callable object type to the traits look does allow
specialisations like ...

template<typename R, typename CallOp, typename ...T>
struct std::coroutine_traits<R, CallOp, T...> {
    struct promise_type {
        promise_type (CallOp op, T ...args) {}
        Fake get_return_object() { return {}; }
        std::suspend_always initial_suspend() { return {}; }
....

to be possible.
( I might be getting out of my depth with the long history of the design
evolution here - but this is my understanding of the rationale for the status
quo ).

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [Bug c++/95540] [coroutine] coroutine_traits<> lookup for lambdas
  2020-06-04 17:03 [Bug c++/95540] New: [coroutine] coroutine_traits<> lookup for lambdas bruck.michael at gmail dot com
                   ` (5 preceding siblings ...)
  2020-06-05 19:19 ` iains at gcc dot gnu.org
@ 2020-06-11  0:17 ` iains at gcc dot gnu.org
  2020-06-11  7:40 ` bruck.michael at gmail dot com
  7 siblings, 0 replies; 9+ messages in thread
From: iains at gcc dot gnu.org @ 2020-06-11  0:17 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95540

--- Comment #7 from Iain Sandoe <iains at gcc dot gnu.org> ---
to summarise.

This doesn't appear to be a bug in GCC (or clang) but something perhaps that
could benefit from a clarifying note in the std?

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [Bug c++/95540] [coroutine] coroutine_traits<> lookup for lambdas
  2020-06-04 17:03 [Bug c++/95540] New: [coroutine] coroutine_traits<> lookup for lambdas bruck.michael at gmail dot com
                   ` (6 preceding siblings ...)
  2020-06-11  0:17 ` iains at gcc dot gnu.org
@ 2020-06-11  7:40 ` bruck.michael at gmail dot com
  7 siblings, 0 replies; 9+ messages in thread
From: bruck.michael at gmail dot com @ 2020-06-11  7:40 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95540

Michael Bruck <bruck.michael at gmail dot com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|NEW                         |RESOLVED
         Resolution|---                         |INVALID

--- Comment #8 from Michael Bruck <bruck.michael at gmail dot com> ---
Yes. Also std::is_lambda_closure<> or an equivalent facility seems to be
missing from the std.

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2020-06-11  7:40 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-06-04 17:03 [Bug c++/95540] New: [coroutine] coroutine_traits<> lookup for lambdas bruck.michael at gmail dot com
2020-06-04 17:24 ` [Bug c++/95540] " bruck.michael at gmail dot com
2020-06-05  7:32 ` iains at gcc dot gnu.org
2020-06-05 15:38 ` bruck.michael at gmail dot com
2020-06-05 15:54 ` iains at gcc dot gnu.org
2020-06-05 18:52 ` bruck.michael at gmail dot com
2020-06-05 19:19 ` iains at gcc dot gnu.org
2020-06-11  0:17 ` iains at gcc dot gnu.org
2020-06-11  7:40 ` bruck.michael at gmail dot com

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).