+2020-05-07 Iain Sandoe <iain@sandoe.co.uk>
+
+ PR c++/94817
+ PR c++/94829
+ * coroutines.cc (morph_fn_to_coro): Set unformed outline
+ functions to error_mark_node. For early error returns suppress
+ warnings about missing ramp return values. Fix reinstatement
+ of the function body on pre-existing initial error.
+ * decl.c (finish_function): Use the normal error path for fails
+ in the ramp function, do not try to compile the helpers if the
+ transform fails.
+
2020-05-07 Marek Polacek <polacek@redhat.com>
PR c++/94590 - Detect long double -> double narrowing.
{
gcc_checking_assert (orig && TREE_CODE (orig) == FUNCTION_DECL);
+ *resumer = error_mark_node;
+ *destroyer = error_mark_node;
if (!coro_function_valid_p (orig))
- return false;
+ {
+ /* For early errors, we do not want a diagnostic about the missing
+ ramp return value, since the user cannot fix this - a 'return' is
+ not allowed in a coroutine. */
+ TREE_NO_WARNING (orig) = true;
+ return false;
+ }
/* We can't validly get here with an empty statement list, since there's no
way for the FE to decide it's a coroutine in the absence of any code. */
tree fnbody = pop_stmt_list (DECL_SAVED_TREE (orig));
- if (fnbody == NULL_TREE)
- return false;
+ gcc_checking_assert (fnbody != NULL_TREE);
/* We don't have the locus of the opening brace - it's filled in later (and
there doesn't really seem to be any easy way to get at it).
if (body_start == NULL_TREE || body_start == error_mark_node)
{
DECL_SAVED_TREE (orig) = push_stmt_list ();
- append_to_statement_list (DECL_SAVED_TREE (orig), &fnbody);
+ append_to_statement_list (fnbody, &DECL_SAVED_TREE (orig));
+ /* Suppress warnings about the missing return value. */
+ TREE_NO_WARNING (orig) = true;
return false;
}
the coroutine promise. */
tree initial_await = build_init_or_final_await (fn_start, false);
if (initial_await == error_mark_node)
- return false;
+ {
+ /* Suppress warnings about the missing return value. */
+ TREE_NO_WARNING (orig) = true;
+ return false;
+ }
/* The type of the frame var for this is the type of its temp proxy. */
tree initial_suspend_type = TREE_TYPE (TREE_OPERAND (initial_await, 1));
tree final_await = build_init_or_final_await (fn_start, true);
if (final_await == error_mark_node)
- return false;
+ {
+ /* Suppress warnings about the missing return value. */
+ TREE_NO_WARNING (orig) = true;
+ return false;
+ }
/* The type of the frame var for this is the type of its temp proxy. */
tree final_suspend_type = TREE_TYPE (TREE_OPERAND (final_await, 1));
{
BIND_EXPR_BODY (ramp_bind) = pop_stmt_list (ramp_body);
DECL_SAVED_TREE (orig) = newbody;
+ /* Suppress warnings about the missing return value. */
+ TREE_NO_WARNING (orig) = true;
return false;
}
bool coro_p = flag_coroutines
&& !processing_template_decl
&& DECL_COROUTINE_P (fndecl);
+ bool coro_emit_helpers = false;
/* When we get some parse errors, we can end up without a
current_function_decl, so cope. */
if (coro_p)
{
- if (!morph_fn_to_coro (fndecl, &resumer, &destroyer))
- {
- DECL_SAVED_TREE (fndecl) = pop_stmt_list (DECL_SAVED_TREE (fndecl));
- poplevel (1, 0, 1);
- DECL_SAVED_TREE (fndecl) = error_mark_node;
- return fndecl;
- }
+ /* Only try to emit the coroutine outlined helper functions if the
+ transforms succeeded. Otherwise, treat errors in the same way as
+ a regular function. */
+ coro_emit_helpers = morph_fn_to_coro (fndecl, &resumer, &destroyer);
/* We should handle coroutine IFNs in middle end lowering. */
cfun->coroutine_component = true;
- if (use_eh_spec_block (fndecl))
+ /* Do not try to process the ramp's EH unless outlining succeeded. */
+ if (coro_emit_helpers && use_eh_spec_block (fndecl))
finish_eh_spec_block (TYPE_RAISES_EXCEPTIONS
(TREE_TYPE (fndecl)),
current_eh_spec_block);
&& !DECL_OMP_DECLARE_REDUCTION_P (fndecl))
cp_genericize (fndecl);
- /* Emit the resumer and destroyer functions now. */
- if (coro_p)
+ /* Emit the resumer and destroyer functions now, providing that we have
+ not encountered some fatal error. */
+ if (coro_emit_helpers)
{
emit_coro_helper (resumer);
emit_coro_helper (destroyer);
+2020-05-07 Iain Sandoe <iain@sandoe.co.uk>
+
+ PR c++/94817
+ PR c++/94829
+ * g++.dg/coroutines/coro-missing-final-suspend.C: New test.
+ * g++.dg/coroutines/coro-missing-initial-suspend.C: New test.
+ * g++.dg/coroutines/coro-missing-promise-yield.C: Check for
+ continuation of compilation.
+ * g++.dg/coroutines/coro-missing-promise.C: Likewise.
+ * g++.dg/coroutines/coro-missing-ret-value.C: Likewise
+ * g++.dg/coroutines/coro-missing-ret-void.C: Likewise
+ * g++.dg/coroutines/coro-missing-ueh-3.C: Likewise
+ * g++.dg/coroutines/pr94817.C: New test.
+ * g++.dg/coroutines/pr94829.C: New test.
+
2020-05-07 Marek Polacek <polacek@redhat.com>
PR c++/94590 - Detect long double -> double narrowing.
--- /dev/null
+// { dg-additional-options "-fsyntax-only -w" }
+#include "coro.h"
+
+// Check diagnostic return from missing promise initial suspend entry.
+
+#define MISSING_FINAL_SUSPEND
+#include "coro1-ret-int-yield-int.h"
+
+coro1
+my_coro () // { dg-error {no member named 'final_suspend' in} }
+{
+ co_return 0;
+}
+
+// check we have not messed up continuation of the compilation.
+template <class... Args>
+struct void_t_imp {
+ using type = void;
+};
--- /dev/null
+// { dg-additional-options "-fsyntax-only -w" }
+#include "coro.h"
+
+// Check diagnostic return from missing promise initial suspend entry.
+
+#define MISSING_INITIAL_SUSPEND
+#include "coro1-ret-int-yield-int.h"
+
+coro1
+my_coro () // { dg-error {no member named 'initial_suspend' in} }
+{
+ co_return 0;
+}
+
+// check we have not messed up continuation of the compilation.
+template <class... Args>
+struct void_t_imp {
+ using type = void;
+};
co_return 0;
}
+// check we have not messed up continuation of the compilation.
+template <class... Args>
+struct void_t_imp {
+ using type = void;
+};
+
int main (int ac, char *av[]) {
MissingPromiseYield x = bar ();
return 0;
co_yield 22; // { dg-error {unable to find the promise type for this coroutine} }
co_return 0;
}
+
+// check we have not messed up continuation of the compilation.
+template <class... Args>
+struct void_t_imp {
+ using type = void;
+};
co_return 6174; // { dg-error {no member named 'return_value' in} }
}
+// check we have not messed up continuation of the compilation.
+template <class... Args>
+struct void_t_imp {
+ using type = void;
+};
+
int main (int ac, char *av[]) {
MissingRetValue x = bar ();
return 0;
co_return; // { dg-error "no member named .return_void. in" }
}
+// check we have not messed up continuation of the compilation.
+template <class... Args>
+struct void_t_imp {
+ using type = void;
+};
+
int main (int ac, char *av[]) {
MissingRetVoid x = bar ();
return 0;
co_return;
}
+// check we have not messed up continuation of the compilation.
+template <class... Args>
+struct void_t_imp {
+ using type = void;
+};
+
int main (int ac, char *av[]) {
MissingUEH x = bar ();
return 0;
return handle_type::from_promise (*this);
}
+#ifdef MISSING_INITIAL_SUSPEND
+#else
auto initial_suspend () {
PRINT ("get initial_suspend (always)");
return suspend_always_prt{};
}
+#endif
+#ifdef MISSING_FINAL_SUSPEND
+#else
auto final_suspend () {
PRINT ("get final_suspend (always)");
return suspend_always_prt{};
}
+#endif
#ifdef USE_AWAIT_TRANSFORM
--- /dev/null
+
+void no_coroutine_traits() {
+ co_await 4; // { dg-error {coroutines require a traits template\; cannot find 'std::coroutine_traits'} }
+}
+
+// check we have not messed up continuation of the compilation.
+template <class... Args>
+struct void_t_imp {
+ using type = void;
+};
--- /dev/null
+
+namespace std::experimental {
+template <typename R, typename... T> struct coroutine_traits {
+ using promise_type = typename R::promise_type;
+};
+
+template <class Promise = void> struct coroutine_handle;
+template <> struct coroutine_handle<void> {
+ static coroutine_handle from_address(void *) noexcept;
+ coroutine_handle() = default;
+ template <class PromiseType>
+ coroutine_handle(coroutine_handle<PromiseType>) noexcept;
+};
+template <class Promise> struct coroutine_handle : coroutine_handle<void> {
+ coroutine_handle() = default;
+ static coroutine_handle from_address(void *) noexcept;
+};
+}
+
+struct suspend_always {
+ bool await_ready() noexcept;
+ void await_suspend(std::experimental::coroutine_handle<>) noexcept;
+ void await_resume() noexcept;
+};
+
+struct Task {
+ struct promise_type {
+ Task get_return_object();
+ void return_void() {}
+ suspend_always initial_suspend() noexcept;
+ suspend_always final_suspend() noexcept;
+ void unhandled_exception() noexcept;
+ };
+};
+
+template <typename _AwrT> auto SyncAwait(_AwrT &&A) {
+ if (!A.await_ready()) {
+ auto AwaitAsync = [&]() -> Task {
+ try { (void)(co_await A); } catch (...) {} // { dg-error {coroutines require a traits template; cannot find 'std::coroutine_traits'} }
+ };
+ Task t = AwaitAsync();
+ }
+ return A.await_resume();
+}
+
+void f() {
+ suspend_always test;
+ SyncAwait(test);
+}