diff mbox

[RFC] . Some initial work on coroutines

Message ID 56DC6196.8030004@gmail.com
State New
Headers show

Commit Message

Mikhail Maltsev March 6, 2016, 4:57 p.m. UTC
Hi, all!

I decided to start working on coroutines support in C++ (according to P0057R2
proposal: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0057r2.pdf).

I did some analysis of how other compilers implement coroutines:
https://github.com/miyuki-chan/coroutines
This github repo has a small example project for Microsoft Visual Studio and
also the relevant part of assembly code generated by MSVC.
It also has a link to Clang testcase for coroutines semantic analysis and
diagnostics, as well as the actual diagnostic output of Clang for that testcase.

In the attached patch I started to work on parsing and semantic analysis. For
now it can parse some basic uses of co_await, co_yield and co_return and
diagnose some erroneous cases. No support for templates, and no 'for co_await' yet.

My main question is: what is the correct way of constructing the AST? I started
looking at how range-based for loops are implemented. As far as I understand,
GCC converts range-based for into normal for loop immediately, when possible,
i.e. when there are no dependent types involved. But when parsing template
definition, GCC may create a node for range-based for loop and convert it during
instantiation. Should we do the same for co_await/co_yield/co_return? Or would
it be better to always construct just AST nodes (perhaps with some
meta-information) and delay actual codegen until we start genericising the
function (apparently, Clang works this way)?

Just it case, what I mean by codegen. The proposal says:
"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)."
So, when we parse "co_yield e", when should we build "co_await p.yield_value(e)"
from it and further expand co_await?
diff mbox

Patch

diff --git a/.gitignore b/.gitignore
index c9a6158..4595d5e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -37,6 +37,9 @@  TAGS.sub
 
 .clang-format
 
+.agignore
+.ycm_extra_conf.py
+
 .gdbinit
 .gdb_history
 
diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index 22ea7da..9962ab5 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -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 },
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index fa3746c..4f753c9 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -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[];
diff --git a/gcc/c-family/c-cppbuiltin.c b/gcc/c-family/c-cppbuiltin.c
index 19999c7..0956d2f 100644
--- a/gcc/c-family/c-cppbuiltin.c
+++ b/gcc/c-family/c-cppbuiltin.c
@@ -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");
     }
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 7c5f6c7..c3e9070 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -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.
diff --git a/gcc/cp/Make-lang.in b/gcc/cp/Make-lang.in
index 2286c64..4c490aa 100644
--- a/gcc/cp/Make-lang.in
+++ b/gcc/cp/Make-lang.in
@@ -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)
diff --git a/gcc/cp/coroutine.cc b/gcc/cp/coroutine.cc
new file mode 100644
index 0000000..18e2fe2
--- /dev/null
+++ b/gcc/cp/coroutine.cc
@@ -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);
+}
+
diff --git a/gcc/cp/cp-tree.def b/gcc/cp/cp-tree.def
index e6e5139..d3dda02 100644
--- a/gcc/cp/cp-tree.def
+++ b/gcc/cp/cp-tree.def
@@ -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:
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index b1dc23c..c6fac43 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -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);
diff --git a/gcc/cp/cxx-pretty-print.c b/gcc/cp/cxx-pretty-print.c
index cc28045..ca834a5 100644
--- a/gcc/cp/cxx-pretty-print.c
+++ b/gcc/cp/cxx-pretty-print.c
@@ -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:
diff --git a/gcc/cp/lex.c b/gcc/cp/lex.c
index e69793e..1e16fb8 100644
--- a/gcc/cp/lex.c
+++ b/gcc/cp/lex.c
@@ -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)
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 535052f..fab2268 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -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.
diff --git a/gcc/function.h b/gcc/function.h
index c4368cd..339a89e 100644
--- a/gcc/function.h
+++ b/gcc/function.h
@@ -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)
 
diff --git a/gcc/testsuite/g++.dg/cpp1z/coroutine1.C b/gcc/testsuite/g++.dg/cpp1z/coroutine1.C
new file mode 100644
index 0000000..4b55dfd
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/coroutine1.C
@@ -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" }
+}