aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '15.0.0/gentoo/70_all_PR116506-coro.patch')
-rw-r--r--15.0.0/gentoo/70_all_PR116506-coro.patch295
1 files changed, 295 insertions, 0 deletions
diff --git a/15.0.0/gentoo/70_all_PR116506-coro.patch b/15.0.0/gentoo/70_all_PR116506-coro.patch
new file mode 100644
index 0000000..4e670fc
--- /dev/null
+++ b/15.0.0/gentoo/70_all_PR116506-coro.patch
@@ -0,0 +1,295 @@
+From 06475fcc5b72fdb9b24e7e39703942e1e3f2f0d5 Mon Sep 17 00:00:00 2001
+From: Iain Sandoe <iain@sandoe.co.uk>
+Date: Thu, 29 Aug 2024 11:18:24 +0100
+Subject: [PATCH] c++, coroutines: Fix awaiter var creation [PR116506].
+
+Awaiters always need to have a coroutine state frame copy since
+they persist across potential supensions. It simplifies the later
+analysis considerably to assign these early which we do when
+building co_await expressions.
+
+The cleanups in r15-3146-g47dbd69b1, unfortunately elided some of
+processing used to cater for cases where the var created from an
+xvalue, or is a pointer/reference type.
+
+Corrected thus.
+
+ PR c++/116506
+ PR c++/116880
+
+gcc/cp/ChangeLog:
+
+ * coroutines.cc (build_co_await): Ensure that xvalues are
+ materialised. Handle references/pointer values in awaiter
+ access expressions.
+
+gcc/testsuite/ChangeLog:
+
+ * g++.dg/coroutines/pr116506.C: New test.
+ * g++.dg/coroutines/pr116880.C: New test.
+
+Signed-off-by: Iain Sandoe <iain@sandoe.co.uk>
+---
+ gcc/cp/coroutines.cc | 82 +++++++++++++++++-----
+ gcc/testsuite/g++.dg/coroutines/pr116506.C | 53 ++++++++++++++
+ gcc/testsuite/g++.dg/coroutines/pr116880.C | 36 ++++++++++
+ 3 files changed, 154 insertions(+), 17 deletions(-)
+ create mode 100644 gcc/testsuite/g++.dg/coroutines/pr116506.C
+ create mode 100644 gcc/testsuite/g++.dg/coroutines/pr116880.C
+
+diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
+index 86a5ac8999a..ba6ee41a4e7 100644
+--- a/gcc/cp/coroutines.cc
++++ b/gcc/cp/coroutines.cc
+@@ -1071,6 +1071,30 @@ build_template_co_await_expr (location_t kw, tree type, tree expr, tree kind)
+ return aw_expr;
+ }
+
++/* For a component ref that is not a pointer type, decide if we can use
++ this directly. */
++static bool
++usable_component_ref (tree comp_ref)
++{
++ if (TREE_CODE (comp_ref) != COMPONENT_REF
++ || TREE_SIDE_EFFECTS (comp_ref))
++ return false;
++
++ while (TREE_CODE (comp_ref) == COMPONENT_REF)
++ {
++ comp_ref = TREE_OPERAND (comp_ref, 0);
++ STRIP_NOPS (comp_ref);
++ /* x-> */
++ if (INDIRECT_REF_P (comp_ref))
++ return false;
++ /* operator-> */
++ if (TREE_CODE (comp_ref) == CALL_EXPR)
++ return false;
++ STRIP_NOPS (comp_ref);
++ }
++ gcc_checking_assert (VAR_P (comp_ref) || TREE_CODE (comp_ref) == PARM_DECL);
++ return true;
++}
+
+ /* This performs [expr.await] bullet 3.3 and validates the interface obtained.
+ It is also used to build the initial and final suspend points.
+@@ -1133,13 +1157,12 @@ build_co_await (location_t loc, tree a, suspend_point_kind suspend_kind,
+ if (o_type && !VOID_TYPE_P (o_type))
+ o_type = complete_type_or_else (o_type, o);
+
+- if (!o_type)
++ if (!o_type || o_type == error_mark_node)
+ return error_mark_node;
+
+ if (TREE_CODE (o_type) != RECORD_TYPE)
+ {
+- error_at (loc, "awaitable type %qT is not a structure",
+- o_type);
++ error_at (loc, "awaitable type %qT is not a structure", o_type);
+ return error_mark_node;
+ }
+
+@@ -1165,20 +1188,47 @@ build_co_await (location_t loc, tree a, suspend_point_kind suspend_kind,
+ if (!glvalue_p (o))
+ o = get_target_expr (o, tf_warning_or_error);
+
+- tree e_proxy = o;
+- if (glvalue_p (o))
++ /* We know that we need a coroutine state frame variable for the awaiter,
++ since it must persist across suspension; so where one is needed, build
++ it now. */
++ tree e_proxy = STRIP_NOPS (o);
++ if (INDIRECT_REF_P (e_proxy))
++ e_proxy = TREE_OPERAND (e_proxy, 0);
++ o_type = TREE_TYPE (e_proxy);
++ bool is_ptr = TYPE_PTR_P (o_type);
++ if (!is_ptr
++ && (TREE_CODE (e_proxy) == PARM_DECL
++ || usable_component_ref (e_proxy)
++ || (VAR_P (e_proxy) && !is_local_temp (e_proxy))))
+ o = NULL_TREE; /* Use the existing entity. */
+- else /* We need to materialise it. */
++ else /* We need a non-temp var. */
+ {
+- e_proxy = get_awaitable_var (suspend_kind, o_type);
+- o = cp_build_init_expr (loc, e_proxy, o);
+- e_proxy = convert_from_reference (e_proxy);
++ tree p_type = o_type;
++ tree o_a = o;
++ if (lvalue_p (o) && !TREE_SIDE_EFFECTS (o))
++ {
++ if (is_ptr)
++ p_type = TREE_TYPE (p_type);
++ p_type = cp_build_reference_type (p_type, false);
++ o_a = build_address (o);
++ }
++ if (INDIRECT_REF_P (o_a))
++ o_a = TREE_OPERAND (o_a, 0);
++ e_proxy = get_awaitable_var (suspend_kind, p_type);
++ o = cp_build_init_expr (loc, e_proxy, o_a);
+ }
+
++ /* Build an expression for the object that will be used to call the awaitable
++ methods. */
++ tree e_object = convert_from_reference (e_proxy);
++ if (TYPE_PTR_P (TREE_TYPE (e_object)))
++ e_object = cp_build_indirect_ref (input_location, e_object, RO_UNARY_STAR,
++ tf_warning_or_error);
++
+ /* I suppose we could check that this is contextually convertible to bool. */
+ tree awrd_func = NULL_TREE;
+ tree awrd_call
+- = build_new_method_call (e_proxy, awrd_meth, NULL, NULL_TREE, LOOKUP_NORMAL,
++ = build_new_method_call (e_object, awrd_meth, NULL, NULL_TREE, LOOKUP_NORMAL,
+ &awrd_func, tf_warning_or_error);
+
+ if (!awrd_func || !awrd_call || awrd_call == error_mark_node)
+@@ -1192,7 +1242,7 @@ build_co_await (location_t loc, tree a, suspend_point_kind suspend_kind,
+ tree h_proxy = get_coroutine_self_handle_proxy (current_function_decl);
+ vec<tree, va_gc> *args = make_tree_vector_single (h_proxy);
+ tree awsp_call
+- = build_new_method_call (e_proxy, awsp_meth, &args, NULL_TREE,
++ = build_new_method_call (e_object, awsp_meth, &args, NULL_TREE,
+ LOOKUP_NORMAL, &awsp_func, tf_warning_or_error);
+
+ release_tree_vector (args);
+@@ -1224,7 +1274,7 @@ build_co_await (location_t loc, tree a, suspend_point_kind suspend_kind,
+ /* Finally, the type of e.await_resume() is the co_await's type. */
+ tree awrs_func = NULL_TREE;
+ tree awrs_call
+- = build_new_method_call (e_proxy, awrs_meth, NULL, NULL_TREE, LOOKUP_NORMAL,
++ = build_new_method_call (e_object, awrs_meth, NULL, NULL_TREE, LOOKUP_NORMAL,
+ &awrs_func, tf_warning_or_error);
+
+ if (!awrs_func || !awrs_call || awrs_call == error_mark_node)
+@@ -1238,7 +1288,7 @@ build_co_await (location_t loc, tree a, suspend_point_kind suspend_kind,
+ return error_mark_node;
+ if (coro_diagnose_throwing_fn (awrs_func))
+ return error_mark_node;
+- if (tree dummy = cxx_maybe_build_cleanup (e_proxy, tf_none))
++ if (tree dummy = cxx_maybe_build_cleanup (e_object, tf_none))
+ {
+ if (CONVERT_EXPR_P (dummy))
+ dummy = TREE_OPERAND (dummy, 0);
+@@ -1249,7 +1299,8 @@ build_co_await (location_t loc, tree a, suspend_point_kind suspend_kind,
+ }
+
+ /* We now have three call expressions, in terms of the promise, handle and
+- 'e' proxies. Save them in the await expression for later expansion. */
++ 'e' proxy expression. Save them in the await expression for later
++ expansion. */
+
+ tree awaiter_calls = make_tree_vec (3);
+ TREE_VEC_ELT (awaiter_calls, 0) = awrd_call; /* await_ready(). */
+@@ -1262,9 +1313,6 @@ build_co_await (location_t loc, tree a, suspend_point_kind suspend_kind,
+ }
+ TREE_VEC_ELT (awaiter_calls, 2) = awrs_call; /* await_resume(). */
+
+- if (REFERENCE_REF_P (e_proxy))
+- e_proxy = TREE_OPERAND (e_proxy, 0);
+-
+ tree awrs_type = TREE_TYPE (TREE_TYPE (awrs_func));
+ tree suspend_kind_cst = build_int_cst (integer_type_node,
+ (int) suspend_kind);
+diff --git a/gcc/testsuite/g++.dg/coroutines/pr116506.C b/gcc/testsuite/g++.dg/coroutines/pr116506.C
+new file mode 100644
+index 00000000000..57a6e360566
+--- /dev/null
++++ b/gcc/testsuite/g++.dg/coroutines/pr116506.C
+@@ -0,0 +1,53 @@
++// { dg-do run }
++// { dg-additional-options "-fno-exceptions" }
++
++#include <coroutine>
++
++bool g_too_early = true;
++std::coroutine_handle<> g_handle;
++
++struct Awaiter {
++ Awaiter() {}
++ ~Awaiter() {
++ if (g_too_early) {
++ __builtin_abort ();
++ }
++ }
++
++ bool await_ready() { return false; }
++ void await_suspend(std::coroutine_handle<> handle) {
++ g_handle = handle;
++ }
++
++ void await_resume() {}
++};
++
++struct SuspendNever {
++ bool await_ready() noexcept { return true; }
++ void await_suspend(std::coroutine_handle<>) noexcept {}
++ void await_resume() noexcept {}
++};
++
++struct Coroutine {
++ struct promise_type {
++ Coroutine get_return_object() { return {}; }
++ SuspendNever initial_suspend() { return {}; }
++ SuspendNever final_suspend() noexcept { return {}; }
++ void return_void() {}
++ void unhandled_exception() {}
++
++ Awaiter&& await_transform(Awaiter&& u) {
++ return static_cast<Awaiter&&>(u);
++ }
++ };
++};
++
++Coroutine foo() {
++ co_await Awaiter{};
++}
++
++int main() {
++ foo();
++ g_too_early = false;
++ g_handle.destroy();
++}
+diff --git a/gcc/testsuite/g++.dg/coroutines/pr116880.C b/gcc/testsuite/g++.dg/coroutines/pr116880.C
+new file mode 100644
+index 00000000000..f0db6a26044
+--- /dev/null
++++ b/gcc/testsuite/g++.dg/coroutines/pr116880.C
+@@ -0,0 +1,36 @@
++#include <coroutine>
++
++struct promise_type;
++using handle_type = std::coroutine_handle<promise_type>;
++
++struct Co {
++ handle_type handle;
++ using promise_type = ::promise_type;
++
++ explicit Co(handle_type handle) : handle(handle) {}
++
++ bool await_ready() { return false; }
++ std::coroutine_handle<> await_suspend(handle_type handle);
++ void await_resume() {}
++};
++
++struct Done {};
++
++struct promise_type {
++ Co get_return_object();
++
++ std::suspend_always initial_suspend() { return {}; };
++ std::suspend_always final_suspend() noexcept { return {}; };
++ void return_value(Done) {}
++ void return_value(Co&&);
++ void unhandled_exception() { throw; };
++ Co&& await_transform(Co&& co) { return static_cast<Co&&>(co); }
++};
++
++Co tryToRun();
++
++Co init()
++{
++ co_await tryToRun();
++ co_return Done{};
++}
+--
+2.39.2 (Apple Git-143)