[Bug libstdc++/115402] std::atomic_ref compile-error in compare_exchange_[weak/strong]() and wait()

2024-06-14 Thread lewissbaker.opensource at gmail dot com via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=115402

--- Comment #7 from Lewis Baker  ---
This paper has now been published and is available at:
https://isocpp.org/files/papers/P3323R0.html

[Bug libstdc++/115402] New: std::atomic_ref compile-error in compare_exchange_[weak/strong]() and wait()

2024-06-09 Thread lewissbaker.opensource at gmail dot com via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=115402

Bug ID: 115402
   Summary: std::atomic_ref compile-error in
compare_exchange_[weak/strong]() and wait()
   Product: gcc
   Version: 15.0
Status: UNCONFIRMED
  Severity: normal
  Priority: P3
 Component: libstdc++
  Assignee: unassigned at gcc dot gnu.org
  Reporter: lewissbaker.opensource at gmail dot com
  Target Milestone: ---

See https://godbolt.org/z/q4jYvPaah

The following code-snippet fails to compile:

 volatile int vi = 0;
 std::atomic_ref vref(vi);
 int val = vref.load(); // ok
 vref.exchange(val); // ok
 vref.compare_exchange_weak(val, 0); // error
 vref.compare_exchange_strong(val, 0); // error
 vref.wait(0); // error

Fails with error messages like:

.../include/c++/15.0.0/bits/atomic_base.h: In instantiation of 'bool
std::__atomic_ref<_Tp, true, false>::compare_exchange_weak(_Tp&, _Tp,
std::memory_order, std::memory_order) const [with _Tp = volatile int]':
/opt/compiler-explorer/gcc-trunk-20240609/include/c++/15.0.0/bits/atomic_base.h:1674:30:
  required from 'bool std::__atomic_ref<_Tp, true,
false>::compare_exchange_weak(_Tp&, _Tp, std::memory_order) const [with _Tp =
volatile int]'
 1674 | return compare_exchange_weak(__expected, __desired, __order,
  |~^~~~
 1675 |  __cmpexch_failure_order(__order));
  |  ~
:8:31:   required from here
8 | vref.compare_exchange_weak(val, 0); // error
  | ~~^~~~
.../include/c++/15.0.0/bits/atomic_base.h:1656:58: error: binding reference of
type 'std::__atomic_impl::_Val&' {aka 'int&'} to 'volatile int'
discards qualifiers
 1656 | return __atomic_impl::compare_exchange_weak(
  |~~^
 1657 |  _M_ptr, __expected, __desired, __success, __failure);
  |  
.../include/c++/15.0.0/bits/atomic_base.h:1119:52: note:   initializing
argument 2 of 'bool std::__atomic_impl::compare_exchange_weak(_Tp*, _Val<_Tp>&,
_Val<_Tp>, std::memory_order, std::memory_order, bool) [with bool _AtomicRef =
true; _Tp = volatile int; _Val<_Tp> = int]'
 1119 |   compare_exchange_weak(_Tp* __ptr, _Val<_Tp>& __expected,
  | ~~~^~

[Bug libstdc++/106183] New: std::atomic::wait might deadlock on platforms without platform_wait()

2022-07-04 Thread lewissbaker.opensource at gmail dot com via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106183

Bug ID: 106183
   Summary: std::atomic::wait might deadlock on platforms without
platform_wait()
   Product: gcc
   Version: unknown
Status: UNCONFIRMED
  Severity: normal
  Priority: P3
 Component: libstdc++
  Assignee: unassigned at gcc dot gnu.org
  Reporter: lewissbaker.opensource at gmail dot com
  Target Milestone: ---

I have been doing some research on implementations of std::atomic::notify_all()
and std::atomic::wait() as part of a C++ paper I've been working on.

I've recently been studying the libc++ implementation and I think I have
discovered a potential bug in the implementation for platforms that do not have
__GLIBCXX_HAVE_PLATFORM_WAIT defined (i.e. that don't have futex syscall or
similar) and for std::atomic types where T is different from
__platform_wait_t.

I believe there is potential for a thread calling x.wait(old) to fail to be
unblocked by a call by another thread to x.notify_all() after modifying the
value to something other than 'old'.

I have reduced the current implementation of the std::atomic::wait() and
std::atomic::notify_all() functions and I believe the code currently in
trunk to be effectively equivalent to the following code-snippet:

--
using __platform_wait_t = std::uint64_t;

struct __waiter_pool {
  std::atomic<__platform_wait_t> _M_wait{0};
  std::mutex _M_mut;
  std::atomic<__platform_wait_t> _M_ver{0};
  std::condition_variable _M_cv;

  static __waiter_pool& _S_for(void* __addr) noexcept {
constexpr uintptr_t __count = 16;
static __waiter_pool __x[__count];
uintptr_t __key = (((uintptr_t)__addr) >> 2) % __count;
return __x[__key];
  }
};

template
bool __atomic_compare(const _Tp& __a, const _Tp& __b) noexcept {
  return std::memcmp(std::addressof(__a), std::addressof(__b), sizeof(_Tp)) ==
0;
}

template
void atomic::wait(T __old, memory_order __mo = memory_order_seq_cst)
noexcept {
  __waiter_pool& __w = __waiter_pool::_S_for(this);
  __w._M_wait.fetch_add(1, std::memory_order_seq_cst);
  do {
__platform_wait_t __val1 = __w._M_ver.load(std::memory_order_acquire);
if (!__atomic_compare(__old, this->load(__mo))) {
return;
}

__platform__wait_t __val2 = __w._M_ver.load(std::memory_order_seq_cst);
// < BUG: problem if notify_all() is executed at this point
if (__val2 == __val1) {
lock_guard __lk(__w._M_mtx);
__w._M_cv.wait(__lk);
}

  } while (__atomic_compare(__old, this->load(__mo)));

  __w._M_wait.fetch_sub(1, std::memory_order_release);
}

void atomic::notify_all() noexcept {
__waiter_pool& __w = __waiter_pool::_S_for(this);
__w._M_ver.fetch_add(1, memory_order_seq_cst);
if (__w._M_wait.load(memory_order_seq_cst) != 0) {
__w._M_cv.notify_all();
}
}
---

The wait() method reads the _M_ver value then checks whether the value being
waited on has changed
and then if it has not, then reads the _M_ver value again. If the two values
read from _M_ver are
the same then we can infer that there has not been an intervening call to
notify_all().

However, after checking that _M_ver has not changed, it then (and only then)
proceeds to acquire
the lock on the _M_mut mutex and then waits on the _M_cv condition variable.

The problem occurs if the waiting thread happens to be delayed between reading
_M_ver for the second
time and blocking inside the call to  _M_cv.wait() (indicated with a comment).
If this happens, it may be possible then for another thread that was supposed
to unblock this thread
to then modify the atomic value, call notify_all() which increments _M_ver and
calls _M_cv.notify_all(),
all before the waiting thread acquires the mutex and blocks on the
condition-variable.

If this happens and no other thread is subsequently going to call notify_all()
on the atomic variable
then it's possible the call to wait() will block forever as it missed its
wake-up call.

The solution here is to do more work while holding the mutex.

I haven't fully verified the correctness of the following code, but I think it
should help to
avoid the missed wake-ups situations that are possible in the current
implementation. It does
come at a higher synchronisation cost, however, as notifying threads also need
to acquire the
mutex.

-

template
void atomic::wait(T __old, memory_order __mo = memory_order_seq_cst)
noexcept {
  __waiter_pool& __w = __waiter_pool::_S_for(this);
  __w._M_wait.fetch_add(1, std::memory_order_seq_cst);
  do {
__platform_wait_t __val1 = __w._M_ver.load(std::memory_order_acquire);
if (!__atomic_compare(__old, this->load(__mo))) {
return;
}

__platform__wait_t __val2 = __w._M_ver.load(std::memory_order_seq_cst);
if (__val2 == __val1) {
lock_guard __lk(__w._M_mtx)

[Bug c++/101247] New: ICE when using static constexpr bool defined in base-class in derived class constructor requires-clause

2021-06-28 Thread lewissbaker.opensource at gmail dot com via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101247

Bug ID: 101247
   Summary: ICE when using static constexpr bool defined in
base-class in derived class constructor
requires-clause
   Product: gcc
   Version: unknown
Status: UNCONFIRMED
  Severity: normal
  Priority: P3
 Component: c++
  Assignee: unassigned at gcc dot gnu.org
  Reporter: lewissbaker.opensource at gmail dot com
  Target Milestone: ---

Compiling the following code with GCC trunk and flags: -std=c++20

```
template
struct in_place_type_t {};

template
inline constexpr in_place_type_t in_place_type;

template
inline constexpr bool outer_helper_v = true;

template
struct foo {
struct type;

template
static constexpr bool helper_v = outer_helper_v;
};

template
struct foo::type {
template
requires helper_v
type(in_place_type_t) {}
};

int main() {
foo::type x(in_place_type);
}
```

results in an internal compiler-error

```
: In substitution of 'template  requires  helper_v
foo::type::type(in_place_type_t) [with U = {int}]':
:26:40:   required from here
:21:18: internal compiler error: tree check: accessed elt 1 of
'tree_vec' with 0 elts in tsubst_pack_expansion, at cp/pt.c:13084
   21 | requires helper_v
  |  ^~~
0x1d4c179 internal_error(char const*, ...)
???:0
0x685746 tree_vec_elt_check_failed(int, int, char const*, int, char const*)
???:0
0x99e0bc tsubst_pack_expansion(tree_node*, tree_node*, int, tree_node*)
???:0
0x9afa62 tsubst_template_args(tree_node*, tree_node*, int, tree_node*)
???:0
0x9b064b tsubst_argument_pack(tree_node*, tree_node*, int, tree_node*)
???:0
0x9afa04 tsubst_template_args(tree_node*, tree_node*, int, tree_node*)
???:0
0x983ebd tsubst(tree_node*, tree_node*, int, tree_node*)
???:0
0x7abcda constraints_satisfied_p(tree_node*, tree_node*)
???:0
0x9c26c3 fn_type_unification(tree_node*, tree_node*, tree_node*, tree_node*
const*, unsigned int, tree_node*, unification_kind_t, int, conversion**, bool,
bool)
???:0
0x74abc9 build_new_method_call(tree_node*, tree_node*, vec**, tree_node*, int, tree_node**, int)
???:0
0x74c8c0 build_special_member_call(tree_node*, tree_node*, vec**, tree_node*, int, int)
???:0
0x8611c8 build_aggr_init(tree_node*, tree_node*, int, int)
???:0
0x81f0d9 cp_finish_decl(tree_node*, tree_node*, bool, tree_node*, int)
???:0
0x94f36d c_parse_file()
???:0
0xad2922 c_common_parse_file()
???:0
```

See https://godbolt.org/z/ebnxfsMs9 for an example.

[Bug c++/97452] [coroutines] incorrect sequencing of await_resume() when multiple co_await expressions occur in a single statement

2021-04-09 Thread lewissbaker.opensource at gmail dot com via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97452

--- Comment #9 from Lewis Baker  ---
> In terms of the standard do you think this is technically undefined behaviour?

Yes, I think this is something that Gor was looking into as a wording issue
that could do with some clarification.

I think the suggestion was something along the lines of adding some wording to
ensure that the evaluation of a an await-expression was sequenced atomically
with respect to the evaluation of other expressions in the statement.

[Bug libstdc++/99537] New: Wrong memory_order used in stop_token ref-counting

2021-03-10 Thread lewissbaker.opensource at gmail dot com via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99537

Bug ID: 99537
   Summary: Wrong memory_order used in stop_token ref-counting
   Product: gcc
   Version: unknown
Status: UNCONFIRMED
  Severity: normal
  Priority: P3
 Component: libstdc++
  Assignee: unassigned at gcc dot gnu.org
  Reporter: lewissbaker.opensource at gmail dot com
  Target Milestone: ---

In the implementation of stop_token the _Stop_state_t implements
reference-counting for tracking shared ownership of the stop-state.

This is done via two methods:

  void
  _M_add_owner() noexcept
  {
_M_owners.fetch_add(1, memory_order::relaxed);
  }

  void
  _M_release_ownership() noexcept
  {
if (_M_owners.fetch_sub(1, memory_order::release) == 1)
  delete this;
  }

Other than initialising the _M_owners atomic value to 1, these are the only two
accesses of the _M_owners variable.

The 'fetch_sub()' operation in _M_release_ownership() should be using
memory_order::acq_rel instead of memory_order::release. The use of 'release'
only is insufficient as it does not synchronise with any corresponding
'acquire' operation.

With the current implementation, it's possible that a prior write to one of the
_M_value or _M_head data-members by a thread releasing the second-to-last
reference might not be visible to another thread that releases the last
reference and frees the memory, resulting in potential write to freed memory.

[Bug c++/97452] New: [coroutines] incorrect sequencing of await_resume() when multiple co_await expressions occur in a single statement

2020-10-15 Thread lewissbaker.opensource at gmail dot com via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97452

Bug ID: 97452
   Summary: [coroutines] incorrect sequencing of await_resume()
when multiple co_await expressions occur in a single
statement
   Product: gcc
   Version: unknown
Status: UNCONFIRMED
  Severity: normal
  Priority: P3
 Component: c++
  Assignee: unassigned at gcc dot gnu.org
  Reporter: lewissbaker.opensource at gmail dot com
  Target Milestone: ---

See https://godbolt.org/z/9Kj9o8

Compile the following program with GCC trunk and flags: -std=c++20


#include 
#include 
#include 
#include 
#include 

struct task {
struct promise_type {
task get_return_object() noexcept {
return
task{std::coroutine_handle::from_promise(*this)};
}
std::suspend_always initial_suspend() noexcept {
std::puts("task initial_suspend");
return {};
}

struct yield_awaiter {
bool await_ready() noexcept { return false; }
auto await_suspend(std::coroutine_handle h) noexcept
{
std::puts("task yielding/returning value");
return std::exchange(h.promise().continuation, {});
}
void await_resume() noexcept {
std::puts("task resumed");
}
};

yield_awaiter yield_value(int x) noexcept {
value = x;
return {};
}

void return_value(int x) noexcept {
value = x;
}

yield_awaiter final_suspend() noexcept {
return {};
}

[[noreturn]] void unhandled_exception() noexcept {
std::terminate();
}

int value;
std::coroutine_handle<> continuation;
};

explicit task(std::coroutine_handle coro) noexcept
: coro(coro)
{}

task(task&& t) noexcept
: coro(std::exchange(t.coro, {}))
{}

~task() {
if (coro) coro.destroy();
}

__attribute__((noinline)) bool await_ready() {
std::puts("task::await_ready");
return false;
}

__attribute__((noinline)) auto await_suspend(std::coroutine_handle<> h)
noexcept {
std::puts("task::await_suspend");
coro.promise().continuation = h;
return coro;
}

__attribute__((noinline)) int await_resume() {
std::puts("task::await_resume");
return coro.promise().value;
}

std::coroutine_handle coro;
};

task two_values() {
co_yield 1;
co_return 2;
}

task example() {
task t = two_values();
int result = co_await t + co_await t;
std::printf("result = %i (should be 3)\n", result);
std::fflush(stdout);
co_return result;
}

int main() {
task t = example();
t.coro.resume();
assert(t.coro.done());
}


Expected output:

task initial_suspend
task initial_suspend
task::await_ready
task::await_suspend
task yielding/returning value
task::await_resume
task::await_ready
task::await_suspend
task resumed
task yielding/returning value
task::await_resume
result = 3 (should be 3)

Actual output:

task initial_suspend
task initial_suspend
task::await_ready
task::await_suspend
task yielding/returning value
task::await_ready
task::await_suspend
task resumed
task yielding/returning value
task::await_resume
task::await_resume
result = 4 (should be 3)


Note that the output indicates the the compiler is only calling await_resume()
on both awaiters immediately before evaluating the plus-expression rather than
evaluating await_resume() on the first awaiter immediately upon resuming from
the first suspend-point and before evaluating await_ready() for the second
awaiter.

[Bug c++/95736] coroutine method improperly copies awaitable

2020-06-18 Thread lewissbaker.opensource at gmail dot com
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95736

Lewis Baker  changed:

   What|Removed |Added

 CC||lewissbaker.opensource@gmai
   ||l.com

--- Comment #1 from Lewis Baker  ---
I've also investigated this a bit more and have annotated the assembly of the
code being generated by GCC in this example (slightly modified).

https://godbolt.org/z/_HJNHC

Immediately before the 'co_await foo' expression the compiler generates
a copy of the this->foo member into the coroutine frame. But it does
this doing the equivalent of a memcpy() rather than calling the
copy-constructor.

The compiler should not be making this copy of the operand to 'co_await'
and should instead be passing the address of the 'this->foo' member to the
as the implicit object parameter in the call to
await_ready/await_suspend/await_resume.

[Bug c++/95616] New: [coroutines] coroutines with potentially-throwing 'co_await promise.final_suspend()' expressions should be ill-formed

2020-06-09 Thread lewissbaker.opensource at gmail dot com
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95616

Bug ID: 95616
   Summary: [coroutines] coroutines with potentially-throwing
'co_await promise.final_suspend()' expressions should
be ill-formed
   Product: gcc
   Version: 10.1.1
Status: UNCONFIRMED
  Severity: normal
  Priority: P3
 Component: c++
  Assignee: unassigned at gcc dot gnu.org
  Reporter: lewissbaker.opensource at gmail dot com
  Target Milestone: ---

The wording of [dcl.fct.def.coroutine]/15 states:
> The expression co_­await promise.final_­suspend() shall not be 
> potentially-throwing ([except.spec]).

See http://eel.is/c++draft/dcl.fct.def.coroutine#15
and http://eel.is/c++draft/except.spec#6

I believe this means that the compiler should reject with a diagnostic any
coroutine with a promise_type where `co_await promise.final_suspend()` is not
noexcept. ie. all of the following must be declared noexcept (if they form part
of the await-expression):
- promise_type::final_suspend()
- finalSuspendObj.operator co_await()
- finalSuspendAwaiter.await_ready()
- finalSuspendAwaiter.await_suspend()
- finalSuspendAwaiter.await_resume()
- finalSuspedObj destructor
- finalSuspendAwaiter destructor

GCC (and Clang) currently accepts code that that does not declare
final_suspend() as
noexcept.

e.g. see https://godbolt.org/z/Et_5eo

[Bug c++/95615] New: [coroutines] Coroutine frame and promise is leaked if exception thrown from promise.initial_suspend()

2020-06-09 Thread lewissbaker.opensource at gmail dot com
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95615

Bug ID: 95615
   Summary: [coroutines] Coroutine frame and promise is leaked if
exception thrown from promise.initial_suspend()
   Product: gcc
   Version: 10.1.1
Status: UNCONFIRMED
  Severity: normal
  Priority: P3
 Component: c++
  Assignee: unassigned at gcc dot gnu.org
  Reporter: lewissbaker.opensource at gmail dot com
  Target Milestone: ---

If an exception is thrown from any of the following:
- promise_type constructor
- promise.get_return_object()
- promise.initial_suspend()
- initSuspendAwaitable.await_ready()
- initSuspendAwaitable.await_suspend()

Then I believe the compiler is required to automatically
destroy the promise (if constructor completed successfully),
destroy the return return-object (if get_return_object() completed
successfully) and free the coroutine frame before letting the
exception propagate to the caller.

However, it seems that this cleanup logic is not currently
begin called by GCC in these situations.

For example, see https://godbolt.org/z/kQWjpF which shows the
behaviour when an exception is thrown from promise.initial_suspend()'s
await_suspend() method (other variants are commented out in the code).


The wording described in [dcl.fct.def.coroutine] p5 in conjunction with p11
seems to indicate that the promise object should be destroyed as the
exception propagates out of the coroutine.

[Bug c++/95599] [coroutines] destructor for temporary operand to co_yield expression called before end of full-expression

2020-06-09 Thread lewissbaker.opensource at gmail dot com
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95599

--- Comment #3 from Lewis Baker  ---
I don't think that whether or not the awaitables or operands to co_yield are
captured by reference / aliased should play into the sequencing of calls to the
destructor.

The destructors of temporaries should always be sequenced to occur at the
semicolon, which will always happen after the coroutine resumes.

For another clearer example, see https://godbolt.org/z/tKQVnb

e.g. changing the example 

--
struct resource {
int x;
resource(int x) : x(x) { std::printf("resource(%i)\n", x); }
~resource() { std::printf("~resource(%i)\n", x); }
resource(resource&&) = delete;
};

generator f() {
(resource{1}, co_yield resource{2});
}
--

Here we are creating temporaries and sequencing expressions using operator,().

The destructor of both resource{1} and resource{2} should both occur at the
semicolon, after the coroutine suspends and resumes, regardless of whether the
object is captured by reference in an awaitable.

ie. the expected output of the modified program should be (this is what clang
does):
```
resource(1)
resource(2)
awaitable::await_suspend()
awaitable::await_resume()
~resource(2)
~resource(1)
```

But the observed output with GCC is:
```
resource(2)
awaitable::await_suspend()
resource(1)
awaitable::await_resume()
~resource(1)
```

Also, I'm not sure why GCC is evaluating the second operand to operator,()
first and why it is constructing resource{1} _after_ the coroutine has
suspended. Nothing should happen in the coroutine body between await_suspend()
and await_resume().

[Bug c++/95599] [coroutines] destructor for temporary operand to co_yield expression called before end of full-expression

2020-06-08 Thread lewissbaker.opensource at gmail dot com
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95599

--- Comment #1 from Lewis Baker  ---
Note there is also some different behaviour if we change the example slightly
by removing awaitable::~awaitable() and leaving the awaitable type with a
trivial destructor.

See https://godbolt.org/z/ff5Uvy

In this case, instead of the resource destructor being called before the
coroutine suspends, the resource destructor is never called.

ie. observed output becomes:
```
resource()
awaitable::await_suspend()
awaitable::await_resume()
```

[Bug c++/95599] New: [coroutines] destructor for temporary operand to co_yield expression called before end of full-expression

2020-06-08 Thread lewissbaker.opensource at gmail dot com
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95599

Bug ID: 95599
   Summary: [coroutines] destructor for temporary operand to
co_yield expression called before end of
full-expression
   Product: gcc
   Version: 10.1.1
Status: UNCONFIRMED
  Severity: normal
  Priority: P3
 Component: c++
  Assignee: unassigned at gcc dot gnu.org
  Reporter: lewissbaker.opensource at gmail dot com
  Target Milestone: ---

If I write the statement:
  co_yield T{};

Then I expect the T constructor to be called before the coroutine suspends
and that the T destructor will not be called until after the coroutine
resumes and execution reaches the semicolon (ie. end of full expression).

However, I am observing that the T destructor is being called before the
await_suspend() method on the awaitable returned from promise.yield_value()
is called (ie. before the coroutine suspends).

To reproduce, compile the following code sample using GCC trunk.
Flags: -std=c++2a -fcoroutines

---
#include 

using namespace std;

#include 
#include 

struct resource {
resource() { std::printf("resource()\n"); }
~resource() { std::printf("~resource()\n"); }
resource(resource&&) = delete;
};

template
struct generator {
struct promise_type {
generator get_return_object() {
return
generator{coroutine_handle::from_promise(*this)};
}

void return_void() {}
void unhandled_exception() {}
suspend_always initial_suspend() { return {}; }
suspend_always final_suspend() { return {}; }

struct awaitable {
resource& r;

awaitable(resource&& r) : r(r) {}

~awaitable() {}

bool await_ready() noexcept { return false; }

void await_suspend(coroutine_handle<> h) noexcept {
std::printf("awaitable::await_suspend()\n");
}

void await_resume() noexcept {
std::printf("awaitable::await_resume()\n");
}
};

awaitable yield_value(resource&& r) {
return awaitable{std::move(r)};
}
};

generator(coroutine_handle coro) : coro(coro)
{}

generator(generator&& g) noexcept : coro(std::exchange(g.coro, {}))
{}

~generator() {
if (coro) { coro.destroy(); }
}

coroutine_handle coro;
};

generator f() {
co_yield resource{};
}

int main() {
generator x = f();
x.coro.resume();
x.coro.resume();
}
---

The expected output of this program is:
```
resource()
awaitable::await_suspend()
awaitable::await_resume()
~resource()
```

However, the observed output of this program is:
```
resource()
~resource()
awaitable::await_suspend()
awaitable::await_resume()
```

i.e. the temporary is not being kept alive until the end
of the full-expression containing the co_yield statement.

[Bug c++/95598] New: [coroutines] Destructor for object returned from get_return_object() never called

2020-06-08 Thread lewissbaker.opensource at gmail dot com
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95598

Bug ID: 95598
   Summary: [coroutines] Destructor for object returned from
get_return_object() never called
   Product: gcc
   Version: 10.1.1
Status: UNCONFIRMED
  Severity: normal
  Priority: P3
 Component: c++
  Assignee: unassigned at gcc dot gnu.org
  Reporter: lewissbaker.opensource at gmail dot com
  Target Milestone: ---

Compile the following code with GCC trunk with flags -std=c++2a -fcoroutines

See https://godbolt.org/z/zTjjGm


#include 
#include 

using namespace std;

struct generator {
struct promise_type {
generator get_return_object() {
return
generator{coroutine_handle::from_promise(*this)};
}

void return_void() {}
void unhandled_exception() {}
suspend_always initial_suspend() { return {}; }
suspend_always final_suspend() { return {}; }
};

generator(coroutine_handle coro) {
std::printf("generator() @ %p\n", this);
}

generator(generator&& g) noexcept {
std::printf("generator(move from %p) @ %p\n", &g, this);
}

~generator() {
std::printf("~generator() @ %p\n", this);
}
};

generator f() {
co_return;
}

int main() {
generator g = f();
}


The expected output of this program is following (assuming copy-elision has 
occurred - this is what Clang outputs):
```
generator() @ 
~generator() @ 
```

Or if copy elision has not occurred, at least the number of
constructor/destructor calls should be balanced:
```
generator() @ 
generator(move from ) @ 
~generator() @ 
~generator() @ 
```

However, the observed output is:
```
generator() @ 
generator(move from ) @ 
~generator() @ 
```

ie. the destructor of the generator constructed at  is never called.

In this case the object constructed at  is the object returned from
promise.get_return_object() and the object constructed at  is the
object returned from the coroutine ramp function f().

It seems that the coroutine ramp function is failing to call the destructor of
the object returned from promise.get_return_object() when the coroutine ramp
function returns.

Normally, this omission of the call to the destructor would not be observed as
the return-value is moved and the destructor of a moved-from object has no
effect, but that is not always the case.

[Bug c++/95591] New: [coroutines] ICE when co_yielding string literal from generator coroutine

2020-06-08 Thread lewissbaker.opensource at gmail dot com
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95591

Bug ID: 95591
   Summary: [coroutines] ICE when co_yielding string literal from
generator coroutine
   Product: gcc
   Version: 10.0
Status: UNCONFIRMED
  Severity: normal
  Priority: P3
 Component: c++
  Assignee: unassigned at gcc dot gnu.org
  Reporter: lewissbaker.opensource at gmail dot com
  Target Milestone: ---

Build the following code sample using GCC trunk.
Command-line flags: -std=c++2a -fcoroutines

--
#include 

using namespace std;

struct generator {
struct promise_type {
generator get_return_object();
void return_void();
void unhandled_exception();
suspend_always initial_suspend();
suspend_always final_suspend();

template
suspend_always yield_value(Arg&&) {
return {};
}
};
};


generator x() {
co_yield "foo";
}
--


Fails with following error:

: In function 'generator x()':
:23:1: internal compiler error: in captures_temporary, at
cp/coroutines.cc:2773
   23 | }
  | ^

See https://godbolt.org/z/UUbcKQ

[Bug c++/95350] New: [coroutines] coroutines with move-only by-value parameters attempt to copy parameter instead of move it

2020-05-26 Thread lewissbaker.opensource at gmail dot com
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95350

Bug ID: 95350
   Summary: [coroutines] coroutines with move-only by-value
parameters attempt to copy parameter instead of move
it
   Product: gcc
   Version: 10.0
Status: UNCONFIRMED
  Severity: normal
  Priority: P3
 Component: c++
  Assignee: unassigned at gcc dot gnu.org
  Reporter: lewissbaker.opensource at gmail dot com
  Target Milestone: ---

The following code fails to compile under GCC 10.0 and GCC trunk.
Compile with -fcoroutines -std=c++2a

See https://godbolt.org/z/zB_RVC

This code compiles successfully under both Clang and MSVC coroutines
implementations.

-
#include 

struct task {
struct promise_type {
task get_return_object();
void return_void();
void unhandled_exception();
std::suspend_always initial_suspend() noexcept;
std::suspend_always final_suspend() noexcept;
};
};

struct move_only {
move_only();
move_only(const move_only&) = delete;
move_only(move_only&) = delete;
move_only(move_only&&) = default;
};

task f(move_only x) {
co_return;
}

-

Fails with compile-error:

: In function 'task f(move_only)':
:30:1: error: use of deleted function 'move_only::move_only(const
move_only&)'
   30 | }
  | ^
:24:5: note: declared here
   24 | move_only(move_only&) = delete;
  | ^

When copying parameters into the coroutine frame which are passed by value
it should be initialising the parameter copy using an xvalue referring to the
original function parameter. Whereas it seems like GCC is attempting to
initialise the parameter-copy with an lvalue-reference to the original
parameter, which ends up calling the copy-constructor.

See http://eel.is/c++draft/dcl.fct.def.coroutine#13
> ... the copy is a variable of type cv T with automatic storage duration
> that is direct-initialized from an xvalue of type T referring to the
> parameter ...

[Bug c++/95346] New: [coroutines] coroutine return-type should be initialised with rvalue if different from get_return_object() return-type

2020-05-26 Thread lewissbaker.opensource at gmail dot com
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95346

Bug ID: 95346
   Summary: [coroutines] coroutine return-type should be
initialised with rvalue if different from
get_return_object() return-type
   Product: gcc
   Version: 10.0
Status: UNCONFIRMED
  Severity: normal
  Priority: P3
 Component: c++
  Assignee: unassigned at gcc dot gnu.org
  Reporter: lewissbaker.opensource at gmail dot com
  Target Milestone: ---

If I have the following code:

--
#include 

struct task {
struct promise_type {
task get_return_object();
void return_void();
void unhandled_exception();
std::suspend_always initial_suspend() noexcept;
std::suspend_always final_suspend() noexcept;
};
};

struct wrapper {
using promise_type = task::promise_type;
wrapper(task&&);
};

wrapper f() {
co_return;
}
--


Then building with GCC 10.0 and GCC trunk with -std=c++20 -fcoroutines fails
with the following compile-error:


x86-64 gcc (trunk)
-O3 -std=c++2a -Wall -fcoroutines
1

x86-64 gcc (trunk) - 368ms
: In function 'wrapper f()':

error: cannot bind rvalue reference of type 'task&&' to lvalue of type 'task'
   30 | }
  | ^
note:   initializing argument 1 of 'wrapper::wrapper(task&&)'
   25 | wrapper(task&&);


I think the return-value of a coroutine should be initialised with an
rvalue-reference to the object returned from get_return_object() if the
two objects have different types. GCC seems to be trying to initialise
the return-value using an lvalue-reference to the object returned from
get_return_object().

Both Clang and MSVC accept this code, while GCC does not.
See https://godbolt.org/z/4RRtaL

[Bug c++/95017] [coroutines] Failure to generate code for co_yield expression if its the only statement in a loop

2020-05-08 Thread lewissbaker.opensource at gmail dot com
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95017

--- Comment #1 from Lewis Baker  ---
Created attachment 48487
  --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=48487&action=edit
test.cpp

[Bug c++/95017] New: [coroutines] Failure to generate code for co_yield expression if its the only statement in a loop

2020-05-08 Thread lewissbaker.opensource at gmail dot com
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95017

Bug ID: 95017
   Summary: [coroutines] Failure to generate code for co_yield
expression if its the only statement in a loop
   Product: gcc
   Version: 10.0
Status: UNCONFIRMED
  Severity: normal
  Priority: P3
 Component: c++
  Assignee: unassigned at gcc dot gnu.org
  Reporter: lewissbaker.opensource at gmail dot com
  Target Milestone: ---

See https://godbolt.org/z/v-Tkvi

Given a simple coroutine generator type, if I compile the following
coroutine with GCC it fails to generate code for the 'co_yield' expression if
the co_yield is the only statement in the loop body.

However, if I add another statement after the co_yield then it correctly
generates code for the co_yield expression.

Works: -std=c++2a -fcoroutines -DWITH_EXTRA_STATEMENT
Fails: -std=c++2a -fcoroutines 

-
generator test()
{
double i = 0;
while(i++ < 5) {
// If the second statement below is compiled-out then GCC fails to
generate
// code for the co_yield expression and the loop completes without
// ever suspending.
co_yield i * 2;

#ifdef WITH_EXTRA_STATEMENT
(void)i;
#endif
}
}

int main() {
for (auto i : test()) {
printf("%f\t", i);
}
}
-

[Bug c++/94752] New: [coroutines] compiler ICE with coroutine with unnamed parameter

2020-04-24 Thread lewissbaker.opensource at gmail dot com
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94752

Bug ID: 94752
   Summary: [coroutines] compiler ICE with coroutine with unnamed
parameter
   Product: gcc
   Version: 10.0
Status: UNCONFIRMED
  Severity: normal
  Priority: P3
 Component: c++
  Assignee: unassigned at gcc dot gnu.org
  Reporter: lewissbaker.opensource at gmail dot com
  Target Milestone: ---

See https://godbolt.org/z/QCXqeo

Compile the following source code with GCC trunk.
Flags: -std=c++20 -fcoroutines

-
#include 
using namespace std;

struct task {
struct promise_type {
promise_type() {}
task get_return_object() { return {}; }
suspend_never initial_suspend() { return {}; }
suspend_never final_suspend() { return {}; }
void return_void() {}
void unhandled_exception() {}
};
};

task foo(int) {
co_return;
}
-

Results in an internal-compile-error - segmentation-fault.
Pointing at closing curly-brace of 'foo()'.

Seems to be related to the parameter being unnamed as naming
the parameter results in this code compiling successfully.

[Bug c++/70790] Can't mangle noexcept expressions

2020-04-07 Thread lewissbaker.opensource at gmail dot com
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70790

Lewis Baker  changed:

   What|Removed |Added

 CC||lewissbaker.opensource@gmai
   ||l.com

--- Comment #2 from Lewis Baker  ---
The relevant code seems to live in gcc/cp/mangle.c (at least for IA64 mangling)

Does it just need something like the following inserted into the
write_expression() function?

```
  else if (TREE_CODE (expr) == NOEXCEPT_EXPR)
{
  write_string ("nx");
  write_expression (TREE_OPERAND (expr, 0));
}
```

The "nx" prefix seems to be what LLVM uses for its Itanium mangling.
See
https://github.com/llvm/llvm-project/blob/13a1504ffb9a9a4f82dc1b60d9e8cbb173c7d030/clang/lib/AST/ItaniumMangle.cpp#L4057

Also see example showing this under clang (working) and gcc (failing):
https://godbolt.org/z/yHrZDm

[Bug c++/92933] New: [coroutines] compiler ICE compiling coroutine with try/catch

2019-12-13 Thread lewissbaker.opensource at gmail dot com
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92933

Bug ID: 92933
   Summary: [coroutines] compiler ICE compiling coroutine with
try/catch
   Product: gcc
   Version: unknown
Status: UNCONFIRMED
  Severity: normal
  Priority: P3
 Component: c++
  Assignee: unassigned at gcc dot gnu.org
  Reporter: lewissbaker.opensource at gmail dot com
  Target Milestone: ---

Created attachment 47490
  --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=47490&action=edit
Example program

Compiling the attached program crashes the compiler with a segmentation fault
when compiling with gcc coroutines branch.

See https://godbolt.org/z/kCNVfQ

This has a simple coroutine that looks like:

struct some_error {};

task foo() {
try {
co_return;
} catch (some_error) {
}
}

Compilation with '-std=c++2a' crashes with the following error:

: In function 'task foo()':
:64:1: internal compiler error: Segmentation fault
   64 | }
  | ^

Removing the try/catch compiles fine.
Replacing the catch(some_error) with catch(...) compiles fine.

[Bug libstdc++/92895] New: [libstdc++] stop_token conformance issues

2019-12-10 Thread lewissbaker.opensource at gmail dot com
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92895

Bug ID: 92895
   Summary: [libstdc++] stop_token conformance issues
   Product: gcc
   Version: unknown
Status: UNCONFIRMED
  Severity: normal
  Priority: P3
 Component: libstdc++
  Assignee: unassigned at gcc dot gnu.org
  Reporter: lewissbaker.opensource at gmail dot com
  Target Milestone: ---

The current implementation of std::stop_token in trunk has a few issues that
make it non-conformant with regards to the latest draft specification N4842.

The version I reviewed was:
https://github.com/gcc-mirror/gcc/blob/87f8610d5d61ceda80ca91be963bdf3e989af4f6/libstdc%2B%2B-v3/include/std/stop_token

Note that there is a reference implementation available here:
https://github.com/josuttis/jthread/blob/master/source/stop_token.hpp


stop_token::stop_possible()

  This implementation is just checking whether the stop_token has
  ownership of a shared-state.
  This is necessary as per [stoptoken.mem]/2.1, but insufficient.

  It also needs to take into account [stoptoken.mem]/2.2 which requires that
this
  method also return false when it does have ownership of a shared state but
where
  there is no associated stop_source.
  This generally requires keeping a separate ref-count for the number of
stop_source
  instances.

stop_token::_Stop_cb::_M_callback

  The function pointer type could be declared noexcept here.
  This might avoid some extra exception-handling codegen being generated in
  _M_request_stop() when this function pointer is invoked.

stop_token::_Stop_cb::_S_execute()

  It is valid for the invocation of a callback registered with the
stop_callback to
  end up calling the destructor of the stop_callback object from within the
callback.

  See [stopcallback.cons]/6 which says:
"... If callback is executing on the current thread, then the destructor
does not block (3.6)
waiting for the return from the invocation of callback. ..."

  The intent of this wording was to indicate that it is potentially valid for
the destructor of
  a stop_callback object to execute on the thread that is currently executing
that instance's
  registered callback. i.e. that the destructor might run inside the
user-provided callback.

  Thus the current logic that tries to update '__cb->_M_prev = __cb->_M_next =
nullptr;' after
  invoking the registered callback is thus potentially writing to a dangling
pointer. You would
  either need to update these pointers before invoking the callback or
otherwise somehow detect
  whether the destructor has been run during the invocation of the callback.

stop_token::_Stop_state_t::_M_request_stop()

  This method holds the _Stop_state_t::_M_mtx lock while invoking user-provided
callbacks.
  This prevents stop_callbacks from being deregistered concurrently while the
user-provided
  callback is executing.

  See [stopcallback.cons]/6 which says:
"The destructor does not block waiting for the execution of another
callback
registered by an associated stop_callback."

  This is necessary to prevent certain deadlock situations that would otherwise
arise were
  a callback to try to acquire a lock on some mutex that might currently be
held by another thread
  that is concurrently trying to deregister a stop_callback.

stop_source::operator=(stop_source&& rhs)

  This implementation just does a swap() which leaves rhs with the previous
state
  of *this rather than leaving rhs as an empty state.

  See [stopcallback.cons]/10 which says that this should instead be equivalent
to:
stop_source(std::move(rhs)).swap(*this).

  ie. you first need to construct a temporary stop_source and call swap() with
that.

stop_callback::stop_callback(const stop_token&, Callback&&)
stop_callback::stop_callback(stop_token&&, Callback&&)

  These constructors are only supposed to throw exceptions that are thrown by
  the constructor of the callback object.

  See [stopcallback.cons]/5 which states:
"Throws: Any exception thrown by the initialization of callback."

  This implementation calls the _Stop_state_t::_M_register_callback()
  method which is not declared noexcept and which calls std::mutex::lock()
which might
  throw a std::system_error if it failed to acquire the mutex lock.

  It might be easier to provide a noexcept implementation here if the
implementation was built
  on top of std::atomic and used std::atomic::wait()-based spin-locks instead
of std::mutex.


  These constructors also have a race-condition that fails to call the callback
being registered
  inline if request_stop() is called concurrently by another thread in-between
the call to
if (__state->_M_stop_requested())
  and
else if (__state->_M_register_callback(this))


stop_callback::~stop_callback()

  This needs to ensure that it blocks until the callback finishes executing if
the
 

[Bug c++/91673] New: GCC ICE when partially specialising class template on a function-signature type with deduced noexcept qualifier

2019-09-05 Thread lewissbaker.opensource at gmail dot com
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91673

Bug ID: 91673
   Summary: GCC ICE when partially specialising class template on
a function-signature type with deduced noexcept
qualifier
   Product: gcc
   Version: unknown
Status: UNCONFIRMED
  Severity: normal
  Priority: P3
 Component: c++
  Assignee: unassigned at gcc dot gnu.org
  Reporter: lewissbaker.opensource at gmail dot com
  Target Milestone: ---

See https://godbolt.org/z/BndP6r

Version: GCC trunk
Command-line: -std=c++1z

Compiling the following code:
```
template
struct overload;

template
struct overload {
using signature_t = Ret(Args...) noexcept(NoExcept);
};

overload x;
```

results in the following output:
```
: In instantiation of 'struct overload':
:9:18:   required from here
:6:11: internal compiler error: in tsubst_copy, at cp/pt.c:16183
6 | using signature_t = Ret(Args...) noexcept(NoExcept);
  |   ^~~
```

This seemed to compile without error under earlier releases of GCC.