@@ -37,6 +37,9 @@ TAGS.sub
.clang-format
+.agignore
+.ycm_extra_conf.py
+
.gdbinit
.gdb_history
@@ -598,6 +598,11 @@ const struct c_common_resword c_common_reswords[] =
{ "concept", RID_CONCEPT, D_CXX_CONCEPTS_FLAGS | D_CXXWARN },
{ "requires", RID_REQUIRES, D_CXX_CONCEPTS_FLAGS | D_CXXWARN },
+ /* C++ coroutines */
+ { "co_await", RID_CO_AWAIT, D_CXX_COROUTINES_FLAGS | D_CXXWARN },
+ { "co_return", RID_CO_RETURN, D_CXX_COROUTINES_FLAGS | D_CXXWARN },
+ { "co_yield", RID_CO_YIELD, D_CXX_COROUTINES_FLAGS | D_CXXWARN },
+
/* These Objective-C keywords are recognized only immediately after
an '@'. */
{ "compatibility_alias", RID_AT_ALIAS, D_OBJC },
@@ -154,6 +154,9 @@ enum rid
/* C++ concepts */
RID_CONCEPT, RID_REQUIRES,
+ /* C++ coroutines */
+ RID_CO_AWAIT, RID_CO_RETURN, RID_CO_YIELD,
+
/* C++ transactional memory. */
RID_ATOMIC_NOEXCEPT, RID_ATOMIC_CANCEL, RID_SYNCHRONIZED,
@@ -383,20 +386,22 @@ extern machine_mode c_default_pointer_mode;
mask) is _true_. Thus for keywords which are present in all
languages the disable field is zero. */
-#define D_CONLY 0x001 /* C only (not in C++). */
-#define D_CXXONLY 0x002 /* C++ only (not in C). */
-#define D_C99 0x004 /* In C, C99 only. */
-#define D_CXX11 0x008 /* In C++, C++11 only. */
-#define D_EXT 0x010 /* GCC extension. */
-#define D_EXT89 0x020 /* GCC extension incorporated in C99. */
-#define D_ASM 0x040 /* Disabled by -fno-asm. */
-#define D_OBJC 0x080 /* In Objective C and neither C nor C++. */
-#define D_CXX_OBJC 0x100 /* In Objective C, and C++, but not C. */
-#define D_CXXWARN 0x200 /* In C warn with -Wcxx-compat. */
-#define D_CXX_CONCEPTS 0x400 /* In C++, only with concepts. */
-#define D_TRANSMEM 0X800 /* C++ transactional memory TS. */
+#define D_CONLY 0x0001 /* C only (not in C++). */
+#define D_CXXONLY 0x0002 /* C++ only (not in C). */
+#define D_C99 0x0004 /* In C, C99 only. */
+#define D_CXX11 0x0008 /* In C++, C++11 only. */
+#define D_EXT 0x0010 /* GCC extension. */
+#define D_EXT89 0x0020 /* GCC extension incorporated in C99. */
+#define D_ASM 0x0040 /* Disabled by -fno-asm. */
+#define D_OBJC 0x0080 /* In Objective C and neither C nor C++. */
+#define D_CXX_OBJC 0x0100 /* In Objective C, and C++, but not C. */
+#define D_CXXWARN 0x0200 /* In C warn with -Wcxx-compat. */
+#define D_CXX_CONCEPTS 0x0400 /* In C++, only with concepts. */
+#define D_TRANSMEM 0x0800 /* C++ transactional memory TS. */
+#define D_CXX_COROUTINES 0x1000 /* C++ coroutines TS. */
#define D_CXX_CONCEPTS_FLAGS D_CXXONLY | D_CXX_CONCEPTS
+#define D_CXX_COROUTINES_FLAGS D_CXXONLY | D_CXX_COROUTINES
/* The reserved keyword table. */
extern const struct c_common_resword c_common_reswords[];
@@ -882,6 +882,8 @@ c_cpp_builtins (cpp_reader *pfile)
/* Use a value smaller than the 201505 specified in
the TS, since we don't yet support atomic_cancel. */
cpp_define (pfile, "__cpp_transactional_memory=210500");
+ if (flag_coroutines)
+ cpp_define (pfile, "__cpp_coroutines=1");
if (flag_sized_deallocation)
cpp_define (pfile, "__cpp_sized_deallocation=201309");
}
@@ -1142,6 +1142,10 @@ fconcepts
C++ ObjC++ Var(flag_concepts)
Enable support for C++ concepts.
+fcoroutines
+C++ ObjC++ Var(flag_coroutines)
+Enable support for C++ coroutines.
+
fcond-mismatch
C ObjC C++ ObjC++
Allow the arguments of the '?' operator to have different types.
@@ -79,7 +79,9 @@ CXX_AND_OBJCXX_OBJS = cp/call.o cp/decl.o cp/expr.o cp/pt.o cp/typeck2.o \
cp/cp-cilkplus.o \
cp/cp-gimplify.o cp/cp-array-notation.o cp/lambda.o \
cp/vtable-class-hierarchy.o cp/constexpr.o cp/cp-ubsan.o \
- cp/constraint.o cp/logic.o $(CXX_C_OBJS)
+ cp/constraint.o cp/logic.o \
+ cp/coroutine.o \
+ $(CXX_C_OBJS)
# Language-specific object files for C++.
CXX_OBJS = cp/cp-lang.o c-family/stub-objc.o $(CXX_AND_OBJCXX_OBJS)
new file mode 100644
@@ -0,0 +1,528 @@
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "hash-set.h"
+#include "machmode.h"
+#include "vec.h"
+#include "double-int.h"
+#include "input.h"
+#include "alias.h"
+#include "symtab.h"
+#include "wide-int.h"
+#include "inchash.h"
+#include "tree.h"
+#include "stringpool.h"
+#include "attribs.h"
+#include "intl.h"
+#include "flags.h"
+#include "cp-tree.h"
+#include "c-family/c-common.h"
+#include "c-family/c-objc.h"
+#include "cp-objcp-common.h"
+#include "tree-inline.h"
+#include "decl.h"
+#include "toplev.h"
+#include "type-utils.h"
+#include "print-tree.h"
+
+// Data associated with a coroutine.
+
+struct coroutine_data // ??? GTY
+{
+ // Location of the first keyword, indicating that this function is a
+ // coroutine.
+ location_t loc;
+ // That keyword (one of "co_yield", "co_return", "co_await")
+ const char *keyword;
+ // Coroutine promise object.
+ tree promise;
+ // Coroutine handle.
+ tree handle;
+ // The final_suspend_label (as defined in the standard) label.
+ tree final_suspend_label;
+};
+
+// Coroutine data of all coroutines in current translation unit.
+// FIXME: avoid another global.
+
+static hash_map<function *, coroutine_data> *coroutines;
+
+#if 0
+static tree
+build_await_temp (tree awaitable)
+{
+ /* Find out the type deduced by the declaration
+ `auto &&__awaitable = awaitable'. */
+ tree awaitable_type = cp_build_reference_type (make_auto (), true);
+ awaitable_type = do_auto_deduction (awaitable_type, awaitable,
+ type_uses_auto (awaitable_type));
+
+ /* Create the __awaitable variable. */
+ tree awaitable_temp = build_decl (input_location, VAR_DECL,
+ get_identifier ("__awaitable"),
+ awaitable_type);
+ TREE_USED (awaitable_temp) = 1;
+ DECL_ARTIFICIAL (awaitable_temp) = 1;
+
+ return awaitable_temp;
+}
+#endif
+
+static inline tree
+lookup_member_fn (tree awaitable_type, tree identifier, tsubst_flags_t complain)
+{
+ return lookup_member (awaitable_type, identifier,
+ /*protect=*/2, /*want_type=*/false, complain);
+}
+
+static inline tree
+build_member_call (tree awaitable, tree ident, vec<tree, va_gc> *args,
+ tsubst_flags_t complain)
+{
+ tree member = finish_class_member_access_expr (awaitable, ident,
+ false, complain);
+ if (member == error_mark_node)
+ return error_mark_node;
+
+ return finish_call_expr (member, &args,
+ /*disallow_virtual=*/false,
+ /*koenig_p=*/false,
+ complain);
+}
+
+static tree
+build_await_expression_1 (tree promise, location_t loc, tree awaitable,
+ bool yield_p, tsubst_flags_t complain)
+{
+ (void)promise;
+ const char *op_name = yield_p ? "co_yield" : "co_await";
+
+ tree id_ready = get_identifier ("await_ready"),
+ id_suspend = get_identifier ("await_suspend"),
+ id_resume = get_identifier ("await_resume");
+
+ tree awaitable_type = TREE_TYPE (awaitable);
+
+ if (!COMPLETE_TYPE_P (complete_type (awaitable_type)))
+ {
+ if (complain & tf_error)
+ error_at (loc, "%<%s%> operand of type %qT has incomplete type",
+ op_name, awaitable_type);
+ return error_mark_node;
+ }
+
+ if (!CLASS_TYPE_P (awaitable_type))
+ {
+ if (complain & tf_error)
+ error_at (loc, "%<%s%> operand of type %qT is not a class",
+ op_name, awaitable_type);
+ return error_mark_node;
+ }
+
+ // create a scope for temporary
+ // tree stmt = begin_compound_statement (/*flags=*/0);
+
+ // TODO: build a temporary, if the awaitable is a prvalue
+ tree awaitable_lv = awaitable;
+
+ tree coro_handle = integer_one_node;
+
+ /* First, lookup await_ready, await_suspend and await_resume (for
+ diagnostics). */
+ tree memb_ready = lookup_member_fn (awaitable_type, id_ready, complain);
+ tree memb_suspend = lookup_member_fn (awaitable_type, id_suspend, complain);
+ tree memb_resume = lookup_member_fn (awaitable_type, id_resume, complain);
+
+ if (!memb_ready || !memb_suspend || !memb_resume)
+ {
+ if (!(complain & tf_error))
+ return error_mark_node;
+
+ // If all three are missing, output a single error
+ if (!memb_ready && !memb_suspend && !memb_resume)
+ {
+ error_at (loc, "%<%s%> operand of type %qT has no "
+ "%<await_ready%>, %<await_suspend%> and "
+ "%<await_resume%> members", op_name, awaitable_type);
+ return error_mark_node;
+ }
+
+ // Otherwise, output an error message for each missing function
+ if (!memb_ready)
+ error_at (loc, "%<%s%> operand of type %qT has no "
+ "%<await_ready%> member", op_name, awaitable_type);
+ if (!memb_suspend)
+ error_at (loc, "%<%s%> operand of type %qT has no "
+ "%<await_suspend%> member", op_name, awaitable_type);
+ if (!memb_resume)
+ error_at (loc, "%<%s%> operand of type %qT has no "
+ "%<await_resume%> member", op_name, awaitable_type);
+ return error_mark_node;
+ }
+
+ vec<tree, va_gc> *args = make_tree_vector ();
+
+ /* - await-ready is the expression e.await_ready(), contextually converted
+ to bool. */
+ tree ready_expr = build_member_call (awaitable_lv, id_ready, args, complain);
+ // — await-resume is the expression e.await_resume().
+ tree resume_expr = build_member_call (awaitable_lv, id_resume, args,
+ complain);
+
+ // - await-suspend is the expression e.await_suspend(h)...
+ vec_safe_push (args, coro_handle);
+ tree suspend_expr = build_member_call (awaitable_lv, id_suspend, args,
+ complain);
+
+ release_tree_vector (args);
+
+ if (ready_expr == error_mark_node
+ || suspend_expr == error_mark_node
+ || resume_expr == error_mark_node)
+ return error_mark_node;
+
+ /* - await-suspend is the expression e.await_suspend(h), which shall be a
+ prvalue of type void or bool. */
+ if (TREE_TYPE (suspend_expr) != void_type_node
+ && TREE_TYPE (suspend_expr) != boolean_type_node)
+ {
+ if (complain & tf_error)
+ error_at (loc, "%<await_suspend%> must return %<bool%> or %<void%> "
+ " (got %qT)", TREE_TYPE (suspend_expr));
+ return error_mark_node;
+ }
+
+ /* We will return a conditional expression :
+ (condition ? ready_expr : else_expr) */
+ tree else_expr, condition;
+ tree suspend_type = cv_unqualified (TREE_TYPE (suspend_expr));
+ if (suspend_type == void_type_node)
+ {
+ /* If the type of await-suspend-expr is cv void, then await-keyword
+ cast-expression is equivalent to:
+
+ (
+ await-ready-expr ? await-resume-expr
+ : (await-suspend-expr, suspend-resume-point,
+ await-resume-expr)
+ ) */
+ condition = ready_expr;
+ else_expr = cp_build_compound_expr (suspend_expr, resume_expr, complain);
+ }
+ else
+ {
+ /* otherwise, it is equivalent to:
+ (
+ (await-ready-expr && !await-suspend-expr)
+ ? await-resume-expr
+ : (suspend-resume-point, await-resume-expr)
+ ) */
+ tree not_suspend = cp_build_unary_op (TRUTH_NOT_EXPR,
+ suspend_expr, /*noconvert=*/false,
+ complain);
+ if (not_suspend == error_mark_node)
+ return error_mark_node;
+
+ condition = build_x_binary_op (loc, TRUTH_AND_EXPR,
+ ready_expr, ERROR_MARK,
+ not_suspend, ERROR_MARK,
+ /*overload=*/NULL, complain);
+ else_expr = resume_expr;
+ }
+
+ if (else_expr == error_mark_node || condition == error_mark_node)
+ return error_mark_node;
+
+ return build_conditional_expr (loc, ready_expr, resume_expr,
+ else_expr, complain);
+}
+
+/* Make function FUN a coroutine and output diagnostics at location LOC in case
+ of error. KW is one of "co_await", "co_yield" or "co_return" - the
+ keyword, which suggests, that current function is a coroutine (used in
+ diagnostics). Return true, if succeeded, or if FUN is already a coroutine.
+ Return false, if failed. */
+
+static bool
+maybe_convert_function_to_coroutine (function *fun, location_t loc,
+ const char *kw)
+{
+ if (fun->is_coroutine)
+ return true;
+
+ tree fun_decl = fun->decl;
+
+ /* 3.6.1 Main function
+ ...
+ The function main shall not be a coroutine (8.4.4).
+ ... */
+ if (DECL_MAIN_P (fun_decl))
+ {
+ error_at (loc, "%<%s%> cannot be used in function %<main%>", kw);
+ return false;
+ }
+
+ /* 7.1.5 The constexpr specifier
+ The definition of a constexpr function shall satisfy the following
+ constraints:
+ ...
+ — it shall not be a coroutine (8.4.4); */
+ if (DECL_DECLARED_CONSTEXPR_P (fun_decl))
+ {
+ error_at (loc, "%<%s%> cannot be used in a constexpr function", kw);
+ return false;
+ }
+
+ if (DECL_CONSTRUCTOR_P (fun_decl))
+ {
+ error_at (loc, "%<%s%> cannot be used in a constructor", kw);
+ return false;
+ }
+
+ if (DECL_DESTRUCTOR_P (fun_decl))
+ {
+ error_at (loc, "%<%s%> cannot be used in a destructor", kw);
+ return false;
+ }
+
+ if (varargs_function_p (fun_decl))
+ {
+ error_at (loc, "%<%s%> cannot be used in a varargs function", kw);
+ return false;
+ }
+
+ tree std_coro_traits
+ = namespace_binding (get_identifier ("coroutine_traits"), std_node);
+ if (!std_coro_traits || !DECL_CLASS_TEMPLATE_P (std_coro_traits))
+ {
+ error_at (loc,
+ "you need to include <coroutine> before defining a coroutine");
+ return false;
+ }
+
+ tree first_parm = DECL_ARGUMENTS (fun->decl);
+ size_t num_parms = 0;
+ for (tree parm = first_parm; parm; parm = TREE_CHAIN (parm), num_parms++)
+ ;
+
+ /* For a coroutine f that is a non-static member function, let P1 denote the
+ type of the implicit object parameter (13.3.1) and P2 ... Pn be the types
+ of the function parameters; otherwise let P1 ... Pn be the types of the
+ function parameters. Let R be the return type and F be the function-body
+ of f, T be the type std::coroutine_traits<R,P1,...,Pn> */
+
+ tree argvec = make_tree_vec (num_parms + 1);
+ TREE_VEC_ELT (argvec, 0) = TREE_TYPE (DECL_RESULT (fun->decl));
+
+ size_t ind = 1;
+ for (tree parm = first_parm; parm; parm = TREE_CHAIN (parm), ind++)
+ TREE_VEC_ELT (argvec, ind) = TREE_TYPE (parm);
+
+ tree coro_traits = lookup_template_class (std_coro_traits, argvec, NULL_TREE,
+ NULL_TREE, 0, tf_warning_or_error);
+ tree promise_type_decl
+ = lookup_member (coro_traits, get_identifier ("promise_type"),
+ /*protect=*/2, /*want_type=*/true, tf_warning_or_error);
+ if (!promise_type_decl)
+ {
+ error_at (loc, "this function cannot be a coroutine: %qT has no "
+ "member named %<promise_type%>",
+ coro_traits);
+ return false;
+ }
+
+ tree promise_type = TREE_TYPE (promise_type_decl);
+
+ if (!COMPLETE_TYPE_P (complete_type (promise_type)))
+ {
+ error_at (loc, "this function cannot be a coroutine: %qT "
+ "has incomplete type",
+ promise_type);
+ return false;
+ }
+
+ if (!CLASS_TYPE_P (promise_type))
+ {
+ error_at (loc, "this function cannot be a coroutine: %qT is not a class",
+ promise_type);
+ return false;
+ }
+
+ // Create the __coro_promise variable
+ tree promise = build_decl (input_location, VAR_DECL,
+ get_identifier ("__coro_promise"), promise_type);
+ TREE_USED (promise) = 1;
+ DECL_ARTIFICIAL (promise) = 1;
+
+ vec<tree, va_gc> *empty_args = make_tree_vector ();
+
+ tree initial_suspend
+ = build_member_call (promise, get_identifier ("initial_suspend"),
+ empty_args, tf_warning_or_error);
+ tree final_suspend
+ = build_member_call (promise, get_identifier ("final_suspend"), empty_args,
+ tf_warning_or_error);
+ if (initial_suspend == error_mark_node || final_suspend == error_mark_node)
+ return false;
+
+ tree initial_await
+ = build_await_expression_1 (promise, loc, initial_suspend,
+ /*yield_p=*/false, tf_warning_or_error);
+ tree final_await
+ = build_await_expression_1 (promise, loc, final_suspend,
+ /*yield_p=*/false, tf_warning_or_error);
+ if (initial_await == error_mark_node || final_await == error_mark_node)
+ return false;
+
+ if (!coroutines)
+ coroutines = new hash_map<function *, coroutine_data>;
+
+ // Memoise promise object of the current coroutine.
+ coroutine_data coro_data = { loc, kw, promise, NULL, NULL };
+ // It sucks, that we can't construct coro_data in-place.
+ coroutines->put (fun, coro_data);
+
+ fun->is_coroutine = true;
+ return true;
+}
+
+static inline tree
+get_promise_object (function *fun)
+{
+ gcc_assert (fun->is_coroutine);
+ return coroutines->get (fun)->promise;
+}
+
+/* Build an await-expression AWAITABLE is cast-expression, operand of co_await
+ operator. LOC is the location of the co_await token. COMPLAIN is a set of
+ flags which is used to suppress errors when performing template argument
+ deduction and substitution (for SFINAE).
+ Return the built expression. */
+
+tree
+build_await_expression (location_t loc, tree awaitable, tsubst_flags_t complain)
+{
+ gcc_checking_assert (!error_operand_p (awaitable));
+
+ function *fun = cfun;
+ gcc_assert (fun && fun->decl == current_function_decl);
+
+ if (!maybe_convert_function_to_coroutine (fun, loc, "co_await"))
+ return error_mark_node;
+
+ tree promise = get_promise_object (fun);
+ return build_await_expression_1 (promise, loc, awaitable,
+ /*yield_p=*/false, complain);
+}
+
+/* Build a yield-expression. OPERAND is an assignment-expression or a
+ braced-init-list, the operand of co_yield. LOC is the location of co_yield
+ token. COMPLAIN plays the same role as in build_await_expression.
+ Return the built expression. */
+
+tree
+build_yield_expression (location_t loc, tree operand, tsubst_flags_t complain)
+{
+ function *fun = cfun;
+
+ if (!maybe_convert_function_to_coroutine (fun, loc, "co_yield"))
+ return error_mark_node;
+
+ /* Let e be the operand of the yield-expression and p be an lvalue naming the
+ promise object of the enclosing coroutine (8.4.4), then the
+ yield-expression is equivalent to the expression
+
+ co_await p.yield_value(e)
+
+ In this function PROMISE variable is 'p', and OPERAND parameter is 'e'. */
+
+ tree promise = get_promise_object (fun);
+
+ vec<tree, va_gc> *args = make_tree_vector ();
+ vec_safe_push (args, operand);
+ tree awaitable = build_member_call (promise, get_identifier ("yield_value"),
+ args, complain);
+ release_tree_vector (args);
+
+ if (awaitable == error_mark_node)
+ return error_mark_node;
+
+ return build_await_expression_1 (promise, loc, awaitable, /*yield_p=*/true,
+ complain);
+}
+
+/* Build a coroutine-return-statement. RET_VALUE is an expression or a
+ braced-init-list, the operand of co_return. NULL, if absent. LOC is the
+ location of co_return token.
+ Return the built statement. */
+
+tree build_coroutine_return_stmt (location_t loc, tree ret_value,
+ tsubst_flags_t complain)
+{
+ function *fun = cfun;
+
+ if (!maybe_convert_function_to_coroutine (fun, loc, "co_return"))
+ return error_mark_node;
+
+ tree promise = get_promise_object (fun);
+
+ /* The expression or braced-init-list of a co_return statement is called its
+ operand. Let p be an lvalue naming the coroutine promise object (8.4.4)
+ and P be the type of that object, then a co_return statement is equivalent
+ to:
+
+ { S; goto final_suspend_label; }
+
+ where f inal_suspend_label is as defined in 8.4.4 and S is an expression
+ defined as follows:
+ — S is p.return_value(braced-init-list), if the operand is a
+ braced-init-list;
+ — S is p.return_value(expression), if the operand is an expression of
+ non-void type;
+ — S is p.return_void(), otherwise; */
+
+ // Name of member function we are going to call
+ const char *mem_fn_name = ret_value ? "return_value" : "return_void";
+
+ vec<tree, va_gc> *args = make_tree_vector ();
+ if (ret_value)
+ vec_safe_push (args, ret_value);
+ tree call_result
+ = build_member_call (promise, get_identifier (mem_fn_name), args, complain);
+ release_tree_vector (args);
+
+ if (call_result == error_mark_node)
+ return error_mark_node;
+
+ // ... S shall be a prvalue of type void.
+ tree result_type = TREE_TYPE (call_result);
+ if (result_type != void_type_node)
+ {
+ if (complain & tf_error)
+ error_at (loc, "%<%s%> must return %<void%> (got %qT)", mem_fn_name,
+ result_type);
+ return error_mark_node;
+ }
+
+ // TODO: build "goto final_suspend_label;"
+ return call_result;
+}
+
+/* Output a diagnostic message, which says that return statement cannot be used
+ in coroutine. LOC is the location of "return" token. */
+
+void diagnose_return_in_coroutine (location_t loc)
+{
+ function *fun = cfun;
+ gcc_assert (fun->is_coroutine);
+
+ coroutine_data *coro_data = coroutines->get (fun);
+ rich_location richloc (line_table, loc);
+ richloc.add_fixit_replace (source_range::from_location (loc), "co_return");
+ error_at_rich_loc (&richloc, "return statement not allowed in coroutine; "
+ "did you mean %<co_return%>?");
+ inform (coro_data->loc, "function is a coroutine due to use of %<%s%> here",
+ coro_data->keyword);
+}
+
@@ -582,6 +582,15 @@ DEFTREECODE (PARM_CONSTR, "parm_constr", tcc_expression, 2)
DEFTREECODE (CONJ_CONSTR, "conj_constr", tcc_expression, 2)
DEFTREECODE (DISJ_CONSTR, "disj_constr", tcc_expression, 2)
+/** Coroutines TS extensions. */
+
+/* await-expression. */
+DEFTREECODE (AWAIT_EXPR, "await_expr", tcc_expression, 1)
+/* yield-expression. */
+DEFTREECODE (YIELD_EXPR, "yield_expr", tcc_expression, 1)
+/* coroutine-return-statement.
+ CO_RETURN_STMT_EXPR has the return expression E. */
+DEFTREECODE (CO_RETURN_STMT, "co_return_stmt", tcc_statement, 1)
/*
Local variables:
@@ -5177,6 +5177,8 @@ enum auto_deduction_context
? TYPE_TI_TEMPLATE (NODE) \
: TYPE_NAME (NODE))
+#define CO_RETURN_STMT_EXPR(NODE) TREE_OPERAND (CO_RETURN_STMT_CHECK (NODE), 0)
+
/* in lex.c */
extern void init_reswords (void);
@@ -6893,6 +6895,15 @@ extern tree decompose_assumptions (tree);
extern tree decompose_conclusions (tree);
extern bool subsumes (tree, tree);
+/* in coroutine.cc */
+extern tree build_await_expression (location_t, tree,
+ tsubst_flags_t);
+extern tree build_yield_expression (location_t, tree,
+ tsubst_flags_t);
+extern tree build_coroutine_return_stmt (location_t, tree,
+ tsubst_flags_t);
+extern void diagnose_return_in_coroutine (location_t);
+
/* in vtable-class-hierarchy.c */
extern void vtv_compute_class_hierarchy_transitive_closure (void);
extern void vtv_generate_init_routine (void);
@@ -826,6 +826,12 @@ cxx_pretty_printer::unary_expression (tree t)
pp_cxx_cast_expression (this, TREE_OPERAND (t, 0));
break;
+ case AWAIT_EXPR:
+ pp_cxx_ws_string (this, "co_await");
+ pp_cxx_whitespace (this);
+ pp_cxx_cast_expression (this, TREE_OPERAND (t, 0));
+ break;
+
default:
c_pretty_printer::unary_expression (t);
break;
@@ -989,6 +995,10 @@ pp_cxx_assignment_operator (cxx_pretty_printer *pp, tree t)
throw-expression:
throw assignment-expression(opt)
+ yield-expression:
+ co_yield assignment-expression
+ co_yield braced-init-list
+
assignment-operator: one of
= *= /= %= += -= >>= <<= &= ^= |= */
@@ -1018,6 +1028,11 @@ cxx_pretty_printer::assignment_expression (tree e)
assignment_expression (TREE_OPERAND (e, 2));
break;
+ case YIELD_EXPR:
+ pp_cxx_ws_string (this, "co_yield");
+ assignment_expression (TREE_OPERAND (e, 0));
+ break;
+
default:
conditional_expression (e);
break;
@@ -1099,6 +1114,7 @@ cxx_pretty_printer::expression (tree t)
case SIZEOF_EXPR:
case ALIGNOF_EXPR:
case NOEXCEPT_EXPR:
+ case AWAIT_EXPR:
unary_expression (t);
break;
@@ -2027,6 +2043,14 @@ cxx_pretty_printer::statement (tree t)
pp_needs_newline (this) = true;
break;
+ case CO_RETURN_STMT:
+ pp_cxx_ws_string (this, "co_return");
+ if (CO_RETURN_STMT_EXPR (t))
+ expression (CO_RETURN_STMT_EXPR (t));
+ pp_cxx_semicolon (this);
+ pp_needs_newline (this) = true;
+ break;
+
/* expression-statement:
expression(opt) ; */
case EXPR_STMT:
@@ -169,6 +169,8 @@ init_reswords (void)
mask |= D_CXX11;
if (!flag_concepts)
mask |= D_CXX_CONCEPTS;
+ if (!flag_coroutines)
+ mask |= D_CXX_COROUTINES;
if (!flag_tm)
mask |= D_TRANSMEM;
if (flag_no_asm)
@@ -2426,6 +2426,11 @@ static bool cp_parser_function_transaction
static tree cp_parser_transaction_cancel
(cp_parser *);
+/* Coroutines Extensions */
+
+static tree cp_parser_yield_expression
+ (cp_parser *);
+
enum pragma_context {
pragma_external,
pragma_member,
@@ -2632,6 +2637,8 @@ static bool cp_parser_array_designator_p
(cp_parser *);
static bool cp_parser_skip_to_closing_square_bracket
(cp_parser *);
+static inline bool assignment_expr_delimiter_p
+ (cpp_ttype type);
/* Concept-related syntactic transformations */
@@ -7640,6 +7647,7 @@ cp_parser_pseudo_destructor_name (cp_parser* parser,
postfix-expression
++ cast-expression
-- cast-expression
+ await-expression [Coroutines]
unary-operator cast-expression
sizeof unary-expression
sizeof ( type-id )
@@ -7825,6 +7833,28 @@ cp_parser_unary_expression (cp_parser *parser, cp_id_kind * pidk,
return finish_noexcept_expr (expr, tf_warning_or_error);
}
+ case RID_CO_AWAIT:
+ {
+ /* await-expression:
+ co_await cast-expression */
+
+ /* FIXME: only warn for unevaluated */
+ if (cp_noexcept_operand)
+ {
+ error_at (token->location, "co_await in noexcept");
+ return error_mark_node;
+ }
+
+ cp_lexer_consume_token (parser->lexer);
+ cp_expr operand = cp_parser_cast_expression (parser,
+ /*address_p=*/false,
+ /*cast_p=*/false,
+ /*decltype=*/false,
+ NULL);
+ return build_await_expression (token->location, operand,
+ tf_warning_or_error);
+ }
+
default:
break;
}
@@ -9027,6 +9057,7 @@ cp_parser_question_colon_clause (cp_parser* parser, cp_expr logical_or_expr)
conditional-expression
logical-or-expression assignment-operator assignment_expression
throw-expression
+ yield-expression
CAST_P is true if this expression is the target of a cast.
DECLTYPE_P is true if this expression is the operand of decltype.
@@ -9043,6 +9074,10 @@ cp_parser_assignment_expression (cp_parser* parser, cp_id_kind * pidk,
a throw-expression. */
if (cp_lexer_next_token_is_keyword (parser->lexer, RID_THROW))
expr = cp_parser_throw_expression (parser);
+ /* Likewise, the `co_yield' keyword denotes the beginning of
+ a yield-expression. */
+ else if (cp_lexer_next_token_is_keyword (parser->lexer, RID_CO_YIELD))
+ expr = cp_parser_yield_expression (parser);
/* Otherwise, it must be that we are looking at a
logical-or-expression. */
else
@@ -10408,6 +10443,7 @@ cp_parser_statement (cp_parser* parser, tree in_statement_expr,
case RID_BREAK:
case RID_CONTINUE:
case RID_RETURN:
+ case RID_CO_RETURN:
case RID_GOTO:
statement = cp_parser_jump_statement (parser);
break;
@@ -11695,6 +11731,7 @@ cp_parser_for_init_statement (cp_parser* parser, tree *decl)
continue ;
return expression [opt] ;
return braced-init-list ;
+ coroutine-return-statement
goto identifier ;
GNU extension:
@@ -11702,6 +11739,10 @@ cp_parser_for_init_statement (cp_parser* parser, tree *decl)
jump-statement:
goto * expression ;
+ coroutine-return-statement:
+ co_return expression [opt] ;
+ co_return braced-init-list ;
+
Returns the new BREAK_STMT, CONTINUE_STMT, RETURN_EXPR, or GOTO_EXPR. */
static tree
@@ -11772,6 +11813,7 @@ cp_parser_jump_statement (cp_parser* parser)
break;
case RID_RETURN:
+ case RID_CO_RETURN:
{
tree expr;
bool expr_non_constant_p;
@@ -11788,8 +11830,24 @@ cp_parser_jump_statement (cp_parser* parser)
/* If the next token is a `;', then there is no
expression. */
expr = NULL_TREE;
- /* Build the return-statement. */
- statement = finish_return_stmt (expr);
+ /* Build the return-statement or coroutine-return-statement. */
+ if (keyword == RID_RETURN)
+ {
+ /* FIXME: this is a prototype. In reality we need to check after
+ parsing the whole function, because otherwise we will not
+ diagnose return statements before coroutine-related keywords. */
+ if (coroutine_p (cfun))
+ {
+ diagnose_return_in_coroutine (token->location);
+ statement = error_mark_node;
+ }
+ else
+ statement = finish_return_stmt (expr);
+ }
+ else
+ statement = build_coroutine_return_stmt (token->location, expr,
+ tf_warning_or_error);
+
/* Look for the final `;'. */
cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON);
}
@@ -23280,6 +23338,19 @@ cp_parser_exception_declaration (cp_parser* parser)
return grokdeclarator (declarator, &type_specifiers, CATCHPARM, 1, NULL);
}
+/* Check whether a token of type TYPE terminates an assignment-expression. */
+
+static inline bool
+assignment_expr_delimiter_p (cpp_ttype type)
+{
+ return (type == CPP_COMMA
+ || type == CPP_SEMICOLON
+ || type == CPP_CLOSE_PAREN
+ || type == CPP_CLOSE_SQUARE
+ || type == CPP_CLOSE_BRACE
+ || type == CPP_COLON);
+}
+
/* Parse a throw-expression.
throw-expression:
@@ -23297,12 +23368,7 @@ cp_parser_throw_expression (cp_parser* parser)
token = cp_lexer_peek_token (parser->lexer);
/* Figure out whether or not there is an assignment-expression
following the "throw" keyword. */
- if (token->type == CPP_COMMA
- || token->type == CPP_SEMICOLON
- || token->type == CPP_CLOSE_PAREN
- || token->type == CPP_CLOSE_SQUARE
- || token->type == CPP_CLOSE_BRACE
- || token->type == CPP_COLON)
+ if (assignment_expr_delimiter_p (token->type))
expression = NULL_TREE;
else
expression = cp_parser_assignment_expression (parser);
@@ -23310,6 +23376,41 @@ cp_parser_throw_expression (cp_parser* parser)
return build_throw (expression);
}
+/* Parse a yield-expression.
+
+ yield-expression:
+ co_yield assignment-expression [opt]
+ co_yield braced-init-list
+
+ Returns a THROW_EXPR representing the throw-expression. */
+
+static tree
+cp_parser_yield_expression (cp_parser *parser)
+{
+ // gcc_checking_assert (cp_lexer_next_token_is (parser->lexer, RID_CO_YIELD));
+
+ cp_token *co_yield_tok = cp_lexer_peek_token(parser->lexer);
+ location_t loc = co_yield_tok->location;
+
+ // Consume co_yield.
+ cp_lexer_consume_token (parser->lexer);
+
+ cp_token *token = cp_lexer_peek_token (parser->lexer);
+ tree expr; // Operand of co_yield
+
+ // Select the right production.
+ if (token->type == CPP_OPEN_BRACE)
+ {
+ cp_lexer_set_source_position (parser->lexer);
+ expr = cp_parser_braced_list (parser, NULL);
+ }
+ else if (assignment_expr_delimiter_p (token->type))
+ expr = NULL_TREE;
+ else
+ expr = cp_parser_assignment_expression (parser);
+ return build_yield_expression (loc, expr, tf_warning_or_error);
+}
+
/* GNU Extensions */
/* Parse an (optional) asm-specification.
@@ -378,6 +378,9 @@ struct GTY(()) function {
/* Set when the tail call has been identified. */
unsigned int tail_call_marked : 1;
+
+ /* Nonzero, if current function is a C++ coroutine (i.e. has side stack). */
+ unsigned int is_coroutine : 1;
};
/* Add the decl D to the local_decls list of FUN. */
@@ -448,6 +451,14 @@ set_loops_for_fn (struct function *fn, struct loops *loops)
fn->x_current_loops = loops;
}
+/* Check, if FN is a coroutine. */
+
+inline bool
+coroutine_p (function *fn)
+{
+ return fn->is_coroutine;
+}
+
/* For backward compatibility... eventually these should all go away. */
#define current_function_funcdef_no (cfun->funcdef_no)
new file mode 100644
@@ -0,0 +1,144 @@
+// { dg-do compile { target c++14 } }
+// { dg-options "-fcoroutines" }
+
+#ifndef __cpp_coroutines
+# error "__cpp_coroutines"
+// FIXME: update when correct value becomes known
+#elif __cpp_coroutines != 1
+# error "__cpp_coroutines != 1"
+#endif
+
+struct test_future
+{
+ bool await_ready ();
+ void await_suspend (int);
+ int await_resume ();
+};
+
+test_future f;
+
+void
+no_coro_traits ()
+{
+ co_await f; // { dg-error "you need to include" }
+}
+
+namespace std {
+template <typename... T> struct coroutine_traits;
+} // namespace std
+
+void
+no_promise_type ()
+{
+ co_await f; // { dg-error "has no member named" }
+}
+
+namespace std {
+template <>
+struct coroutine_traits<void, int>
+{
+ typedef int promise_type;
+};
+} // namespace std
+
+void
+promise_not_class (int)
+{
+ co_await f; // { dg-error "is not a class" }
+}
+
+struct promise_fwd;
+
+namespace std {
+template <>
+struct coroutine_traits<void, short>
+{
+ using promise_type = ::promise_fwd;
+};
+} // namespace std
+
+void
+promise_incomplete (short)
+{
+ co_await f; // { dg-error "has incomplete type" }
+}
+
+namespace std {
+template <>
+struct coroutine_traits<void, long>
+{
+ struct promise_type { };
+};
+} // namespace std
+
+void
+promise_no_suspend (long)
+{
+ co_await f; // { dg-error "has no member named" }
+}
+
+constexpr void
+constexpr_coro ()
+{
+ co_await f; // { dg-error "cannot be used in a constexpr function" }
+}
+
+void
+varargs_coro (int, ...)
+{
+ co_return; // { dg-error "cannot be used in a varargs function" }
+}
+
+struct coro_cdtor
+{
+ coro_cdtor ()
+ {
+ co_await f; // { dg-error "cannot be used in a constructor" }
+ }
+ ~coro_cdtor ()
+ {
+ co_yield 1; // { dg-error "cannot be used in a destructor" }
+ }
+};
+
+namespace std {
+template <>
+struct coroutine_traits<double, double>
+{
+ struct promise_type
+ {
+ test_future initial_suspend ();
+ test_future final_suspend ();
+ void return_value (double);
+ };
+};
+} // namespace std
+
+double
+return_in_coro (double)
+{
+ co_await f; // { dg-message "function is a coroutine" }
+ return 1; // { dg-error "return statement not allowed" }
+}
+
+double
+await_not_class (double)
+{
+ co_await 1; // { dg-error "is not a class" }
+}
+
+struct bad_awaitable
+{
+ bool await_ready ();
+};
+
+double
+bad_await (double)
+{
+ co_await bad_awaitable(); // { dg-error "has no" }
+}
+
+int main ()
+{
+ co_await f; // { dg-error "cannot be used" }
+}