coroutines: Fix handling of ramp return value [PR94661]

Coroutine ramp functions have synthesised return values (the
user-authored function body cannot have an explicit 'return').
The current implementation attempts to optimise by building
the return in-place, in the manner of C++17 code. Clearly,
that was too ambitious and the fix builds a target expr for
the constructed version and passes that to finish_return_stmt.

This also means that we now get the same error messages for
implicit use of deleted CTORs etc.

gcc/cp/ChangeLog:

2020-04-21 Iain Sandoe <iain@sandoe.co.uk>

	PR c++/94661
	* coroutines.cc (morph_fn_to_coro): Simplify return
	value computation.

gcc/testsuite/ChangeLog:

	2020-04-21 Iain Sandoe <iain@sandoe.co.uk>

	PR c++/94661
	* g++.dg/coroutines/ramp-return-a.C: New test.
	* g++.dg/coroutines/ramp-return-b.C: New test.
	* g++.dg/coroutines/ramp-return-c.C: New test.
This commit is contained in:
Iain Sandoe 2020-04-21 10:35:13 +01:00
parent e6cbe9654d
commit cd08718d57
7 changed files with 159 additions and 31 deletions

View File

@ -1,3 +1,9 @@
2020-04-21 Iain Sandoe <iain@sandoe.co.uk>
PR c++/94661
* coroutines.cc (morph_fn_to_coro): Simplify return
value computation.
2020-04-17 Marek Polacek <polacek@redhat.com>
PR c++/94592

View File

@ -3726,23 +3726,14 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
}
tree gro_context_body = push_stmt_list ();
tree gro, gro_bind_vars;
if (same_type_p (TREE_TYPE (get_ro), fn_return_type))
{
gro = DECL_RESULT (orig);
gro_bind_vars = NULL_TREE; /* We don't need a separate var. */
}
else
{
gro = build_lang_decl (VAR_DECL, get_identifier ("coro.gro"),
TREE_TYPE (TREE_OPERAND (get_ro, 0)));
DECL_CONTEXT (gro) = current_scope ();
r = build_stmt (fn_start, DECL_EXPR, gro);
add_stmt (r);
gro_bind_vars = gro; /* We need a temporary var. */
}
tree gro = build_lang_decl (VAR_DECL, get_identifier ("coro.gro"),
TREE_TYPE (get_ro));
DECL_CONTEXT (gro) = current_scope ();
add_decl_expr (gro);
tree gro_bind_vars = gro;
/* Initialize our actual var. */
/* We have to sequence the call to get_return_object before initial
suspend. */
r = build2_loc (fn_start, INIT_EXPR, TREE_TYPE (gro), gro, get_ro);
r = coro_build_cvt_void_expr_stmt (r, fn_start);
add_stmt (r);
@ -3779,30 +3770,22 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
logically doing things related to the end of the function. */
/* The ramp is done, we just need the return value. */
if (!same_type_p (TREE_TYPE (gro), fn_return_type))
if (!same_type_p (TREE_TYPE (get_ro), fn_return_type))
{
/* construct the return value with a single GRO param. */
vec<tree, va_gc> *args = make_tree_vector_single (gro);
r = build_special_member_call (DECL_RESULT (orig),
r = build_special_member_call (NULL_TREE,
complete_ctor_identifier, &args,
fn_return_type, LOOKUP_NORMAL,
tf_warning_or_error);
r = coro_build_cvt_void_expr_stmt (r, input_location);
add_stmt (r);
release_tree_vector (args);
r = build_cplus_new (fn_return_type, r, tf_warning_or_error);
}
/* Else the GRO is the return and we already built it in place. */
else
r = rvalue (gro); /* The GRO is the return value. */
bool no_warning;
r = check_return_expr (DECL_RESULT (orig), &no_warning);
if (error_operand_p (r) && warn_return_type)
/* Suppress -Wreturn-type for the ramp. */
TREE_NO_WARNING (orig) = true;
finish_return_stmt (r);
r = build_stmt (input_location, RETURN_EXPR, DECL_RESULT (orig));
TREE_NO_WARNING (r) |= no_warning;
r = maybe_cleanup_point_expr_void (r);
add_stmt (r);
/* Finish up the ramp function. */
BIND_EXPR_VARS (gro_context_bind) = gro_bind_vars;
BIND_EXPR_BODY (gro_context_bind) = pop_stmt_list (gro_context_body);
BIND_EXPR_BODY (ramp_bind) = pop_stmt_list (ramp_body);

View File

@ -1,3 +1,10 @@
2020-04-21 Iain Sandoe <iain@sandoe.co.uk>
PR c++/94661
* g++.dg/coroutines/ramp-return-a.C: New test.
* g++.dg/coroutines/ramp-return-b.C: New test.
* g++.dg/coroutines/ramp-return-c.C: New test.
2020-04-17 Marek Polacek <polacek@redhat.com>
PR c++/94592

View File

@ -0,0 +1,24 @@
// { dg-additional-options "-std=c++14" }
#include "ramp-return.h"
task<int>
foo ()
{
std::coroutine_handle<promise<int>> _handle;
return task<int> (_handle);
}
// This ICEd for the PR.
task<int>
bar ()
{
co_return 0;
}
task<std::vector<int>>
baz ()
{
co_return std::vector<int>();
}

View File

@ -0,0 +1,22 @@
// { dg-options "-fcoroutines -std=c++14" }
#define DELETE_COPY_CTOR 1
#include "ramp-return.h"
task<int>
foo ()
{
std::coroutine_handle<promise<int>> _handle;
return task<int> (_handle); // { dg-error {use of deleted function 'task<T>::task\(const task<T>&\) \[with T = int\]'} }
}
task<int>
bar ()
{
co_return 0;
} // { dg-error {use of deleted function 'task<T>::task\(const task<T>&\) \[with T = int\]'} }
task<std::vector<int>>
baz ()
{
co_return std::vector<int>();
} // { dg-error {use of deleted function 'task<T>::task\(const task<T>&\) \[with T = std::vector<int>\]'} }

View File

@ -0,0 +1,22 @@
// { dg-additional-options "-std=c++17" }
#define DELETE_COPY_CTOR 1
#include "ramp-return.h"
task<int>
foo ()
{
std::coroutine_handle<promise<int>> _handle;
return task<int> (_handle);
}
task<int>
bar ()
{
co_return 0;
}
task<std::vector<int>>
baz ()
{
co_return std::vector<int>();
}

View File

@ -0,0 +1,64 @@
#include "coro.h"
#include <exception>
#include <vector>
template <typename T>
struct promise {
T _value;
coro::coroutine_handle<> _continuation = nullptr;
struct final_awaitable {
bool _has_continuation;
final_awaitable(bool has_continuation)
: _has_continuation(has_continuation) {}
bool await_ready() const noexcept { return !_has_continuation; }
template <typename Promise>
coro::coroutine_handle<>
await_suspend(coro::coroutine_handle<Promise> coro) noexcept {
return coro.promise()._continuation;
}
void await_resume() noexcept {}
};
auto get_return_object() noexcept {
return coro::coroutine_handle<promise>::from_promise(*this);
}
auto initial_suspend() noexcept { return coro::suspend_always(); }
auto final_suspend() noexcept {
return final_awaitable(_continuation != nullptr);
}
void return_value(T value) { _value = value; }
void unhandled_exception() { std::terminate(); }
};
template <typename T>
struct task {
using promise_type = promise<T>;
std::coroutine_handle<promise<T>> _handle;
task (coro::coroutine_handle<promise<T>> handle) : _handle(handle) {}
#if DELETE_COPY_CTOR
task (const task &) = delete; // no copying
#endif
#if DELETE_MOVE_CTOR
task(task&& t) noexcept
: _handle(t._handle) { t._handle = nullptr; }
#endif
bool await_ready() noexcept { return _handle.done(); }
std::coroutine_handle<>
await_suspend(std::coroutine_handle<> handle) noexcept {
_handle.promise()._continuation = handle;
return _handle;
}
T await_resume() noexcept { return _handle.promise()._value; }
};