Hi, Since coroutine-ness is discovered lazily, we encounter the diagnostics during each keyword parse. We were not handling the case where a user code failed to include fundamental information (e.g. the traits) in a graceful manner (nor tolerating that level of fail for subsequent diagnostics).
Once we've emitted an error for this level of fail, then we suppress additional copies (otherwise the same thing will be reported for every coroutine keyword seen). tested on x86_64-darwin16, OK for trunk? thanks iain gcc/cp/ChangeLog: 2020-01-28 Iain Sandoe <i...@sandoe.co.uk> * coroutines.cc (get_coroutine_info): Tolerate fatal errors that might leave the info unset. (find_coro_traits_template_decl): Note we emitted the error and suppress duplicates. (coro_promise_type_found_p): Reorder initialization so that we check for the traits and their usability before allocation of the info table. Check for a suitable return type and emit a diagnostic for here instead of relying on the lookup machinery. This allows the error to have a better location, and means we can suppress multiple copies. (coro_function_valid_p): Re-check for a valid promise (and thus the traits) before proceeding. Tolerate missing info as a fatal error. gcc/testsuite/ChangeLog: 2020-01-28 Iain Sandoe <i...@sandoe.co.uk> * g++.dg/coroutines/pr93458-1-missing-traits.C: New test. * g++.dg/coroutines/pr93458-2-bad-coro-type.C: New test. --- gcc/cp/coroutines.cc | 76 ++++++++++++++----- .../coroutines/pr93458-1-missing-traits.C | 10 +++ .../coroutines/pr93458-2-bad-coro-type.C | 12 +++ 3 files changed, 78 insertions(+), 20 deletions(-) create mode 100644 gcc/testsuite/g++.dg/coroutines/pr93458-1-missing-traits.C create mode 100644 gcc/testsuite/g++.dg/coroutines/pr93458-2-bad-coro-type.C diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc index e8a6a4033f6..ca86c7e6950 100644 --- a/gcc/cp/coroutines.cc +++ b/gcc/cp/coroutines.cc @@ -169,7 +169,8 @@ get_or_insert_coroutine_info (tree fn_decl) coroutine_info * get_coroutine_info (tree fn_decl) { - gcc_checking_assert (coroutine_info_table != NULL); + if (coroutine_info_table == NULL) + return NULL; coroutine_info **slot = coroutine_info_table->find_slot_with_hash (fn_decl, coroutine_info_hasher::hash (fn_decl), NO_INSERT); @@ -255,11 +256,16 @@ static GTY(()) tree void_coro_handle_type; static tree find_coro_traits_template_decl (location_t kw) { + static bool error_emitted = false; tree traits_decl = lookup_qualified_name (std_node, coro_traits_identifier, 0, true); - if (traits_decl == NULL_TREE || traits_decl == error_mark_node) + /* If we are missing fundmental information, such as the traits, then don't + emit this for every keyword in a TU. */ + if (!error_emitted && + (traits_decl == NULL_TREE || traits_decl == error_mark_node)) { error_at (kw, "cannot find %<coroutine traits%> template"); + error_emitted = true; return NULL_TREE; } else @@ -370,34 +376,45 @@ coro_promise_type_found_p (tree fndecl, location_t loc) { gcc_assert (fndecl != NULL_TREE); - /* Save the coroutine data on the side to avoid the overhead on every - function decl. */ - - /* We only need one entry per coroutine in a TU, the assumption here is that - there are typically not 1000s. */ if (!coro_initialized) { - gcc_checking_assert (coroutine_info_table == NULL); - /* A table to hold the state, per coroutine decl. */ - coroutine_info_table = - hash_table<coroutine_info_hasher>::create_ggc (11); - /* Set up the identifiers we will use. */ - gcc_checking_assert (coro_traits_identifier == NULL); + /* Trees we only need to create once. + Set up the identifiers we will use. */ coro_init_identifiers (); - /* Trees we only need to create once. */ + /* Coroutine traits template. */ coro_traits_templ = find_coro_traits_template_decl (loc); - gcc_checking_assert (coro_traits_templ != NULL); + if (coro_traits_templ == NULL_TREE + || coro_traits_templ == error_mark_node) + return false; + /* coroutine_handle<> template. */ coro_handle_templ = find_coro_handle_template_decl (loc); - gcc_checking_assert (coro_handle_templ != NULL); + if (coro_handle_templ == NULL_TREE + || coro_handle_templ == error_mark_node) + return false; + /* We can also instantiate the void coroutine_handle<> */ void_coro_handle_type = instantiate_coro_handle_for_promise_type (loc, NULL_TREE); - gcc_checking_assert (void_coro_handle_type != NULL); + if (void_coro_handle_type == NULL_TREE + || void_coro_handle_type == error_mark_node) + return false; + + /* A table to hold the state, per coroutine decl. */ + gcc_checking_assert (coroutine_info_table == NULL); + coroutine_info_table = + hash_table<coroutine_info_hasher>::create_ggc (11); + + if (coroutine_info_table == NULL) + return false; + coro_initialized = true; } + /* Save the coroutine data on the side to avoid the overhead on every + function decl tree. */ + coroutine_info *coro_info = get_or_insert_coroutine_info (fndecl); /* Without this, we cannot really proceed. */ gcc_checking_assert (coro_info); @@ -407,6 +424,19 @@ coro_promise_type_found_p (tree fndecl, location_t loc) { /* Get the coroutine traits template class instance for the function signature we have - coroutine_traits <R, ...> */ + if (!CLASS_TYPE_P (TREE_TYPE (TREE_TYPE (fndecl)))) + { + static bool coro_type_error_emitted = false; + /* It makes more sense to show the function header for this, even + though we will have encountered it when processing a keyword. + Only emit the error once, not for every keyword we encounter. */ + if (!coro_type_error_emitted) + error_at (DECL_SOURCE_LOCATION (fndecl), "a coroutine must have a" + " class or struct return type"); + coro_type_error_emitted = true; + return false; + } + tree templ_class = instantiate_coro_traits (fndecl, loc); /* Find the promise type for that. */ @@ -422,7 +452,7 @@ 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); - if (handle_type == NULL_TREE) + if (handle_type == NULL_TREE || handle_type == error_mark_node) return false; /* Complete this, we're going to use it. */ @@ -597,11 +627,17 @@ coro_function_valid_p (tree fndecl) { location_t f_loc = DECL_SOURCE_LOCATION (fndecl); + /* For cases where fundamental information cannot be found, e.g. the + coroutine traits are missing, we need to punt early. */ + if (!coro_promise_type_found_p (fndecl, f_loc)) + return false; + /* Since we think the function is a coroutine, that implies we parsed a keyword that triggered this. Keywords check promise validity for their context and thus the promise type should be known at this point. */ - gcc_checking_assert (get_coroutine_handle_type (fndecl) != NULL_TREE - && get_coroutine_promise_type (fndecl) != NULL_TREE); + if (get_coroutine_handle_type (fndecl) == NULL_TREE + || get_coroutine_promise_type (fndecl) == NULL_TREE) + return false; if (current_function_returns_value || current_function_returns_null) { diff --git a/gcc/testsuite/g++.dg/coroutines/pr93458-1-missing-traits.C b/gcc/testsuite/g++.dg/coroutines/pr93458-1-missing-traits.C new file mode 100644 index 00000000000..46adccded98 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/pr93458-1-missing-traits.C @@ -0,0 +1,10 @@ +// { dg-additional-options "-fsyntax-only -fexceptions -w" } + +// Diagose missing traits (fail to include <coroutine>). + +int +bad_coroutine (void) +{ + co_yield 5; // { dg-error {cannot find 'coroutine traits' template} } + co_return; +} diff --git a/gcc/testsuite/g++.dg/coroutines/pr93458-2-bad-coro-type.C b/gcc/testsuite/g++.dg/coroutines/pr93458-2-bad-coro-type.C new file mode 100644 index 00000000000..aab9b34b69a --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/pr93458-2-bad-coro-type.C @@ -0,0 +1,12 @@ +// { dg-additional-options "-fsyntax-only -fexceptions -w" } + +// Diagose bad coroutine function type. + +#include "coro.h" + +int +bad_coroutine (void) // { dg-error {a coroutine must have a class or struct return type} } +{ + co_yield 5; + co_return; +} -- 2.24.1