diff options
author | Arsen Arsenović <arsen@aarsen.me> | 2024-07-25 22:41:34 +0200 |
---|---|---|
committer | Arsen Arsenović <arsen@gcc.gnu.org> | 2024-08-02 20:33:58 +0200 |
commit | 5b4476a165565cb20729c0a97a3f43b060595209 (patch) | |
tree | 1dcc07017fe8ee47579bcd5fd5c3b03ccc9619ad /gcc/cp | |
parent | a10436a8404ad2f0cc5aa4d6a0cc850abe5ef49e (diff) | |
download | gcc-5b4476a165565cb20729c0a97a3f43b060595209.zip gcc-5b4476a165565cb20729c0a97a3f43b060595209.tar.gz gcc-5b4476a165565cb20729c0a97a3f43b060595209.tar.bz2 |
c++/coroutines: check for members we use in handle_types [PR105475]
Currently, it is possible to ICE GCC by giving it sufficiently broken
code, where sufficiently broken means a std::coroutine_handle missing a
default on the promise_type template argument, and missing members.
As the code generator relies on lookups in the coroutine_handle never
failing (and has no way to signal that error), lets do it ahead of time,
save the result, and use that. This saves us some lookups and allows us
to propagate an error.
PR c++/105475 - coroutines: ICE in coerce_template_parms, at cp/pt.cc:9183
gcc/cp/ChangeLog:
PR c++/105475
* coroutines.cc (struct coroutine_info): Add from_address.
Carries the from_address member we looked up earlier.
(coro_resume_identifier): Remove. Unused.
(coro_init_identifiers): Do not initialize the above.
(void_coro_handle_address): New variable. Contains the baselink
for the std::coroutine_handle<void>::address() instance method.
(get_handle_type_address): New function. Looks up and validates
handle_type::address in a given handle_type.
(get_handle_type_from_address): New function. Looks up and
validates handle_type::from_address in a given handle_type.
(coro_promise_type_found_p): Remove reliance on
coroutine_handle<> defaulting the promise type to void. Store
get_handle_type_* results where appropriate.
(get_coroutine_from_address): New helper. Gets the
handle_type::from_address BASELINK from a coroutine_info.
(build_actor_fn): Use the get_coroutine_from_address helper and
void_coro_handle_address.
gcc/testsuite/ChangeLog:
PR c++/105475
* g++.dg/coroutines/pr103868.C: Add std::coroutine_handle
members we check for now.
* g++.dg/coroutines/pr105287.C: Ditto.
* g++.dg/coroutines/pr105301.C: Ditto.
* g++.dg/coroutines/pr94528.C: Ditto.
* g++.dg/coroutines/pr94879-folly-1.C: Ditto.
* g++.dg/coroutines/pr94883-folly-2.C: Ditto.
* g++.dg/coroutines/pr98118.C: Ditto.
* g++.dg/coroutines/pr105475.C: New test.
* g++.dg/coroutines/pr105475-1.C: New test.
* g++.dg/coroutines/pr105475-2.C: New test.
* g++.dg/coroutines/pr105475-3.C: New test.
* g++.dg/coroutines/pr105475-4.C: New test.
* g++.dg/coroutines/pr105475-5.C: New test.
* g++.dg/coroutines/pr105475-6.C: New test.
* g++.dg/coroutines/pr105475-broken-spec.C: New test.
* g++.dg/coroutines/pr105475-broken-spec-2.C: New test.
Diffstat (limited to 'gcc/cp')
-rw-r--r-- | gcc/cp/coroutines.cc | 138 |
1 files changed, 124 insertions, 14 deletions
diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc index af03f5e..742f0e5 100644 --- a/gcc/cp/coroutines.cc +++ b/gcc/cp/coroutines.cc @@ -91,6 +91,7 @@ struct GTY((for_user)) coroutine_info one that will eventually be allocated in the coroutine frame. */ tree promise_proxy; /* Likewise, a proxy promise instance. */ + tree from_address; /* handle_type from_address function. */ tree return_void; /* The expression for p.return_void() if it exists. */ location_t first_coro_keyword; /* The location of the keyword that made this function into a coroutine. */ @@ -203,7 +204,6 @@ static GTY(()) tree coro_final_suspend_identifier; static GTY(()) tree coro_return_void_identifier; static GTY(()) tree coro_return_value_identifier; static GTY(()) tree coro_yield_value_identifier; -static GTY(()) tree coro_resume_identifier; static GTY(()) tree coro_address_identifier; static GTY(()) tree coro_from_address_identifier; static GTY(()) tree coro_get_return_object_identifier; @@ -243,7 +243,6 @@ coro_init_identifiers () coro_return_void_identifier = get_identifier ("return_void"); coro_return_value_identifier = get_identifier ("return_value"); coro_yield_value_identifier = get_identifier ("yield_value"); - coro_resume_identifier = get_identifier ("resume"); coro_address_identifier = get_identifier ("address"); coro_from_address_identifier = get_identifier ("from_address"); coro_get_return_object_identifier = get_identifier ("get_return_object"); @@ -271,6 +270,7 @@ coro_init_identifiers () static GTY(()) tree coro_traits_templ; static GTY(()) tree coro_handle_templ; static GTY(()) tree void_coro_handle_type; +static GTY(()) tree void_coro_handle_address; /* ================= Parse, Semantics and Type checking ================= */ @@ -389,7 +389,97 @@ find_coro_handle_template_decl (location_t kw) return handle_decl; } -/* Instantiate the handle template for a given promise type. */ +/* Get and validate HANDLE_TYPE::address. The resulting function, if any, will + be a non-overloaded member function that takes no arguments and returns + void*. If that is not the case, signals an error and returns NULL_TREE. */ + +static tree +get_handle_type_address (location_t kw, tree handle_type) +{ + tree addr_getter = lookup_member (handle_type, coro_address_identifier, 1, + 0, tf_warning_or_error); + if (!addr_getter || addr_getter == error_mark_node) + { + qualified_name_lookup_error (handle_type, coro_address_identifier, + error_mark_node, kw); + return NULL_TREE; + } + + if (!BASELINK_P (addr_getter) + || TREE_CODE (TREE_TYPE (addr_getter)) != METHOD_TYPE) + { + error_at (kw, "%qE must be a non-overloaded method", addr_getter); + return NULL_TREE; + } + + tree fn_t = TREE_TYPE (addr_getter); + tree arg = TYPE_ARG_TYPES (fn_t); + + /* Skip the 'this' pointer. */ + arg = TREE_CHAIN (arg); + + /* Check that from_addr has the argument list (). */ + if (arg != void_list_node) + { + error_at (kw, "%qE must take no arguments", addr_getter); + return NULL_TREE; + } + + tree ret_t = TREE_TYPE (fn_t); + if (!same_type_p (ret_t, ptr_type_node)) + { + error_at (kw, "%qE must return %qT, not %qT", + addr_getter, ptr_type_node, ret_t); + return NULL_TREE; + } + + return addr_getter; +} + +/* Get and validate HANDLE_TYPE::from_address. The resulting function, if + any, will be a non-overloaded static function that takes a single void* and + returns HANDLE_TYPE. If that is not the case, signals an error and returns + NULL_TREE. */ + +static tree +get_handle_type_from_address (location_t kw, tree handle_type) +{ + tree from_addr = lookup_member (handle_type, coro_from_address_identifier, 1, + 0, tf_warning_or_error); + if (!from_addr || from_addr == error_mark_node) + { + qualified_name_lookup_error (handle_type, coro_from_address_identifier, + error_mark_node, kw); + return NULL_TREE; + } + if (!BASELINK_P (from_addr) + || TREE_CODE (TREE_TYPE (from_addr)) != FUNCTION_TYPE) + { + error_at (kw, "%qE must be a non-overloaded static function", from_addr); + return NULL_TREE; + } + + tree fn_t = TREE_TYPE (from_addr); + tree arg = TYPE_ARG_TYPES (fn_t); + /* Check that from_addr has the argument list (void*). */ + if (!arg + || !same_type_p (TREE_VALUE (arg), ptr_type_node) + || TREE_CHAIN (arg) != void_list_node) + { + error_at (kw, "%qE must take a single %qT", from_addr, ptr_type_node); + return NULL_TREE; + } + + tree ret_t = TREE_TYPE (fn_t); + if (!same_type_p (ret_t, handle_type)) + { + error_at (kw, "%qE must return %qT, not %qT", + from_addr, handle_type, ret_t); + return NULL_TREE; + } + + return from_addr; +} static tree instantiate_coro_handle_for_promise_type (location_t kw, tree promise_type) @@ -453,11 +543,16 @@ ensure_coro_initialized (location_t loc) return false; /* We can also instantiate the void coroutine_handle<> */ - void_coro_handle_type = - instantiate_coro_handle_for_promise_type (loc, NULL_TREE); + void_coro_handle_type + = instantiate_coro_handle_for_promise_type (loc, void_type_node); if (void_coro_handle_type == NULL_TREE) return false; + void_coro_handle_address + = get_handle_type_address (loc, void_coro_handle_type); + if (!void_coro_handle_address) + return false; + /* A table to hold the state, per coroutine decl. */ gcc_checking_assert (coroutine_info_table == NULL); coroutine_info_table = @@ -552,13 +647,17 @@ coro_promise_type_found_p (tree fndecl, location_t loc) } /* Try to find the handle type for the promise. */ - tree handle_type = - instantiate_coro_handle_for_promise_type (loc, coro_info->promise_type); + tree handle_type + = instantiate_coro_handle_for_promise_type (loc, coro_info->promise_type); if (handle_type == NULL_TREE) return false; + tree from_address = get_handle_type_from_address (loc, handle_type); + if (from_address == NULL_TREE) + return false; /* Complete this, we're going to use it. */ coro_info->handle_type = complete_type_or_else (handle_type, fndecl); + coro_info->from_address = from_address; /* Diagnostic would be emitted by complete_type_or_else. */ if (!coro_info->handle_type) @@ -675,6 +774,15 @@ get_coroutine_promise_proxy (tree decl) } static tree +get_coroutine_from_address (tree decl) +{ + if (coroutine_info *info = get_coroutine_info (decl)) + return info->from_address; + + return NULL_TREE; +} + +static tree lookup_promise_method (tree fndecl, tree member_id, location_t loc, bool musthave) { @@ -2232,7 +2340,6 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody, { verify_stmt_tree (fnbody); /* Some things we inherit from the original function. */ - tree handle_type = get_coroutine_handle_type (orig); tree promise_type = get_coroutine_promise_type (orig); tree promise_proxy = get_coroutine_promise_proxy (orig); @@ -2392,8 +2499,9 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody, tree ash = build_class_member_access_expr (actor_frame, ash_m, NULL_TREE, false, tf_warning_or_error); /* So construct the self-handle from the frame address. */ - tree hfa_m = lookup_member (handle_type, coro_from_address_identifier, 1, - 0, tf_warning_or_error); + tree hfa_m = get_coroutine_from_address (orig); + /* Should have been set earlier by coro_promise_type_found_p. */ + gcc_assert (hfa_m); r = build1 (CONVERT_EXPR, build_pointer_type (void_type_node), actor_fp); vec<tree, va_gc> *args = make_tree_vector_single (r); @@ -2488,12 +2596,14 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody, r = build_stmt (loc, LABEL_EXPR, continue_label); add_stmt (r); + /* Should have been set earlier by the coro_initialized code. */ + gcc_assert (void_coro_handle_address); + /* We want to force a tail-call even for O0/1, so this expands the resume call into its underlying implementation. */ - tree addr = lookup_member (void_coro_handle_type, coro_address_identifier, - 1, 0, tf_warning_or_error); - addr = build_new_method_call (continuation, addr, NULL, NULL_TREE, - LOOKUP_NORMAL, NULL, tf_warning_or_error); + tree addr = build_new_method_call (continuation, void_coro_handle_address, + NULL, NULL_TREE, LOOKUP_NORMAL, NULL, + tf_warning_or_error); tree resume = build_call_expr_loc (loc, builtin_decl_explicit (BUILT_IN_CORO_RESUME), 1, addr); |