Wording For Coroutines: Document Number: Date: Revises: Audience: Authors
Wording For Coroutines: Document Number: Date: Revises: Audience: Authors
Date: 2016-02-12
Revises: P0057R1
Audience: CWG / LEWG
Authors: Gor Nishanov <[email protected]>
Jens Maurer <[email protected]>
Richard Smith <[email protected]>
Daveed Vandevoorde <[email protected]>
Note: this is an early draft. It’s known to be incomplet and incorrekt, and it has lots of ba d
formatting.
c ISO/IEC P0057R2
Contents
Contents ii
1 General 1
1.1 Scope . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2 Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.3 Normative references . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.4 Implementation compliance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.5 Feature testing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.9 Program execution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
2 Lexical conventions 3
2.12 Keywords . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
3 Basic concepts 4
3.6 Start and termination . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
5 Expressions 5
5.3 Unary expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
5.18 Assignment and compound assignment operators . . . . . . . . . . . . . . . . . . . . . . . 6
5.21 Yield . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
6 Statements 8
6.5 Iteration statements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
6.6 Jump statements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
7 Declarations 10
8 Declarators 11
8.4 Function definitions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
13 Overloading 15
13.5 Overloaded operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
13.6 Built-in operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
Contents ii
c ISO/IEC P0057R2
List of Tables
1 Feature-test macro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1 General [intro]
1.1 Scope [intro.scope]
1 This Technical Specification describes extensions to the C++ Programming Language (1.3) that enable
definition of coroutines. These extensions include new syntactic forms and modifications to existing language
semantics.
2 The International Standard, ISO/IEC 14882, provides important context and specification for this Technical
Specification. This document is written as a set of changes against that specification. Instructions to modify
or add paragraphs are written as explicit instructions. Modifications made directly to existing text from the
International Standard use underlining to represent added text and strikethrough to represent deleted text.
§ 1.9 1
c ISO/IEC P0057R2
7 An instance of each object with automatic storage duration (3.7.3) is associated with each entry
into its block. Such an object exists and retains its last-stored value during the execution of the
block and while the block is suspended (by a call of a function, suspension of a coroutine (5.3.8),
or receipt of a signal).
§ 1.9 2
c ISO/IEC P0057R2
§ 2.12 3
c ISO/IEC P0057R2
§ 3.6.1 4
c ISO/IEC P0057R2
5 Expressions [expr]
5.3 Unary expressions [expr.unary]
Add await-expression to the grammar production unary-expression:
unary-expression:
postfix-expression
++ cast-expression
-- cast-expression
await-expression
unary-operator cast-expression
sizeof unary-expression
sizeof ( type-id )
sizeof ... ( identifier )
alignof ( type-id )
noexcept-expression
new-expression
delete-expression
§ 5.3.8 5
c ISO/IEC P0057R2
my_future<void> g() {
std::cout << "just about go to sleep...\n";
co_await 10ms;
std::cout << "resumed\n";
}
— end example ]
§ 5.18 6
c ISO/IEC P0057R2
my_generator<pair<int,int>> g1() {
for (int i = i; i < 10; ++i) co_yield {i,i};
}
my_generator<pair<int,int>> g2() {
for (int i = i; i < 10; ++i) co_yield make_pair(i,i);
}
auto f(int x = co_yield 5); // error: yield-expression outside of function suspension context
int a[] = { co_yield 1 }; // error: yield-expression outside of function suspension context
int main() {
auto r1 = g1();
auto r2 = g2();
assert(std::equal(r1.begin(), r1.end(), r2.begin(), r2.end()));
}
— end example ]
§ 5.21 7
c ISO/IEC P0057R2
6 Statements [stmt.stmt]
6.5 Iteration statements [stmt.iter]
Add the underlined text to paragraph 1.
1 Iteration statements specify looping.
iteration-statement:
while ( condition ) statement
do statement while ( expression ) ;
for ( for-init-statement conditionopt ; expressionopt ) statement
for co_awaitopt ( for-range-declaration : for-range-initializer ) statement
let range-init be equivalent to the braced-init-list. In each case, a range-based for statement is
equivalent to
{
auto && __range = range-init;
for ( auto __begin = co_awaitopt begin-expr,
__end = end-expr;
__begin != __end;
co_awaitopt ++__begin ) {
for-range-declaration = *__begin;
statement
}
}
where co_await is present if and only if it appears immediately after the for keyword, and
__range, __begin, and __end are variables defined for exposition only, and _RangeT is the type
of the expression, and begin-expr and end-expr are determined as follows: ...
Add the following paragraph after paragraph 2.
3 A range-based for statement with co_await shall appear only within a suspension context of a
function (5.3.8).
1) this ensures that a top-level comma operator cannot be reinterpreted as a delimiter between init-declarators in the decla-
ration of __range.
§ 6.5.4 8
c ISO/IEC P0057R2
§ 6.6.3.1 9
c ISO/IEC P0057R2
7 Declarations [dcl.dcl]
7.1.5 The constexpr specifier [dcl.constexpr]
Insert a new bullet after paragraph 3 bullet 1.
3 The definition of a constexpr function shall satisfy the following constraints:
(3.1) — it shall not be virtual (10.3);
(3.2) — it shall not be a coroutine (8.4.4);
(3.3) — ...
§ 7.1.6.4 10
c ISO/IEC P0057R2
8 Declarators [dcl.decl]
8.4 Function definitions [dcl.fct.def]
8.4.4 Coroutines [dcl.fct.def.coroutine]
Add this section to 8.4.
1 A function is a coroutine if it contains a coroutine-return-statement (6.6.3.1), an await-expression
(5.3.8), a yield-expression (5.21), or a range-based for (6.5.4) with co_await. The parameter-
declaration-clause of the coroutine shall not terminate with an ellipsis that is not part of a
parameter-declaration.
2 [ Example:
task<int> f();
task<void> g1() {
int i = co_await f();
std::cout << "f() => " << i << std::endl;
}
— end example ]
3 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 >, and P be the class
type denoted by T ::promise_type. Then, the coroutine behaves as if its body were:
{
P p;
co_await p .initial_suspend(); // initial suspend point
F’
final_suspend :
co_await p .final_suspend(); // final suspend point
}
where F’ is
try { F } catch(...) { p .set_exception(std::current_exception()); }
if the unqualified-id set_exception is found in the scope of P by class member access lookup
(3.4.5), and F’ is F otherwise. An object denoted as p is the promise object of the coroutine and
its type P is the promise type of the coroutine.
§ 8.4.4 11
c ISO/IEC P0057R2
4 The unqualified-ids return_void and return_value are looked up in the scope of class P . If
both are found, the program is ill-formed. If the unqualified-id return_void is found, flowing
off the end of a coroutine is equivalent to a co_return with no operand. Otherwise, flowing off
the end of a coroutine results in undefined behavior.
5 When a coroutine returns to its caller, the return value is obtained by a call to p.get_return_-
object(). A call to a get_return_object is sequenced before the call to initial_suspend and
is invoked at most once.
6 A suspended coroutine can be resumed to continue execution by invoking a resumption member
function (18.11.2.4) of an object of type coroutine_handle<P > associated with this instance
of the coroutine. The function that invoked a resumption member function is called resumer.
Invoking a resumption member function for a coroutine that is not suspended results in undefined
behavior.
7 An implementation may need to allocate additional storage for the lifetime of a coroutine. This
storage is known as the coroutine state and is obtained by calling a non-array allocation func-
tion (3.7.4.1). The allocation function’s name is looked up in the scope of P . If this lookup
fails, the allocation function’s name is looked up in the global scope. If the lookup finds an
allocation function that takes exactly one parameter, it will be used; otherwise, all parameters of
the coroutine are passed to the allocation function after the size parameter in order. [ Note: An
allocation function template shall have two or more function parameters. A template instance
is never considered to be an allocation function with exactly one parameter, regardless of its
signature. — end note ]
8 The coroutine state is destroyed when control flows off the end of the coroutine or the destroy
member function (18.11.2.4) of an object of type std::coroutine_handle<P > associated with
this coroutine is invoked. In the latter case objects with automatic storage duration that are in
scope at the suspend point are destroyed in the reverse order of the construction. The dynamically
allocated storage is released by calling a non-array deallocation function (3.7.4.2). If destroy is
called for a coroutine that is not suspended, the program has undefined behavior.
9 The deallocation function’s name is looked up in the scope of P . If this lookup fails, the dealloca-
tion function’s name is looked up in the global scope. If deallocation function lookup finds both a
usual deallocation function with only a pointer parameter and a usual deallocation function with
both a pointer parameter and a size parameter, then the selected deallocation function shall be
the one with two parameters. Otherwise, the selected deallocation function shall be the function
with one parameter. If no usual deallocation function is found, the program is ill-formed.
10 When a coroutine is invoked, an implementation may create a copy of one or more coroutine
parameters. Each such copy is direct-initialized from an lvalue referring to the corresponding
parameter if it is an lvalue reference, and an xvalue referring to it otherwise. A reference to a
parameter in the function-body of the coroutine is replaced by a reference to its copy.
11 The unqualified-id get_return_object_on_allocation_failure is looked up in the scope of
class P by class member access lookup (3.4.5). If a declaration is found, then std::nothrow_t
forms of allocation and deallocation functions are used. If an allocation function returns nullptr,
the coroutine returns control to the caller of the coroutine and the return value is obtained by a
call to P ::get_return_object_on_allocation_failure().
[ Example:
// using nothrow operator new
struct generator {
using handle = std::coroutine_handle<promise_type>;
struct promise_type {
int current_value;
§ 8.4.4 12
c ISO/IEC P0057R2
int main() {
auto g = f();
while (g.move_next()) std::cout << g.current_value() << std::endl;
}
— end example ]
12 [ Example:
// using a stateful allocator
class Arena;
struct my_coroutine {
struct promise_type {
...
template <typename... TheRest>
void* operator new(std::size_t size, Arena& pool, TheRest const&...) {
return pool.allocate(size);
}
void* operator delete(void* p, std::size_t size) {
// reference to a pool is not available
// to the delete operator and should be stored
// by the allocator as a part of the allocation
return Arena::deallocate(p, size);
}
};
};
my_coroutine (Arena& a) {
// will call my_coroutine::promise_type::operator new(<required-size>, a)
// to obtain storage for the coroutine state
co_yield 1;
}
int main() {
Pool memPool;
for (int i = 0; i < 1’000’000; ++i) my_coroutine(memPool);
};
— end example ]
§ 8.4.4 13
c ISO/IEC P0057R2
§ 12.8 14
c ISO/IEC P0057R2
13 Overloading [over]
13.5 Overloaded operators [over.oper]
Add co_await to the list of operators in paragraph 1 before operators () and [].
§ 13.6 15
c ISO/IEC P0057R2
§ 18.11 16
c ISO/IEC P0057R2
// 18.11.2.2 export/import
void* address() const noexcept;
static coroutine_handle from_address(void* addr) noexcept;
§ 18.11.2 17
c ISO/IEC P0057R2
// 18.11.2.3 capacity
explicit operator bool() const noexcept;
// 18.11.2.4 resumption
void operator()() const;
void resume() const;
void destroy() const;
1 Let P be a promise type of the coroutine (8.4.4). An object of type coroutine_handle<P > is
called a coroutine handle and can be used to refer to a suspended or executing coroutine. A
default constructed coroutine_handle object does not refer to any coroutine.
18.11.2.1 coroutine_handle construct/reset [coroutine.handle.con]
constexpr coroutine_handle() noexcept;
constexpr coroutine_handle(nullptr_t) noexcept;
1 Postconditions: address() == nullptr.
static coroutine_handle coroutine_handle::from_promise(Promise& p) noexcept;
2 Requires: p is a reference to a promise object of a coroutine.
3 Returns: coroutine handle h refering to the coroutine.
4 Postconditions: addressof(h.promise()) == addressof(p).
coroutine_handle& operator=(nullptr_t) noexcept;
5 Postconditions: address() == nullptr.
6 Returns: *this.
§ 18.11.2.3 18
c ISO/IEC P0057R2
§ 18.11.2.8 19
c ISO/IEC P0057R2
§ 18.11.3 20