diff options
Diffstat (limited to '15.0.0/gentoo/70_all_PR116506-coro.patch')
-rw-r--r-- | 15.0.0/gentoo/70_all_PR116506-coro.patch | 295 |
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) |