@@ -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)
{
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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;
+}