Reputation: 1209
I have a question about Visual Studio 2015 implementation of the Coroutines TS. P0057r5 working paper states that the coroutine behaves as if its body were:
{
P p;
auto gro = p.get_return_object();
co_await p.initial_suspend(); // initial suspend point
F’
final_suspend:
co_await p.final_suspend(); // final suspend point
}
(§ 8.4.4\3)
and that when a coroutine returns to its caller, the return value is produced as if by the statement return gro;
(§ 8.4.4\5)
Please note that result of p.get_return_object()
is stored in variable with auto
deduced type.
Let coroutine return type be A
and promise.get_return_object()
return type be B
. According to P0057r5 mentioned above variable gro
should have type B
(deduced by auto
) and object of type A
should be constructed from gro
when coroutine returns to its caller (for example, using conversion operator in B
or implicit constructor from B
in A
).
In current Visual Studio implementation (compiler version string: "Microsoft (R) C/C++ Optimizing Compiler Version 19.00.24215.1 for x86") conversion is done after p.initial_suspend()
and before F'
gets called as if gro
type was set to match return type of the coroutine (A
) and not the return type of promise.get_return_object()
(B
).
Am I missing something or is this a bug?
Minimal example (compile with /await):
#include <experimental/coroutine>
#include <iostream>
struct foo;
struct foo_builder {
foo_builder() {
std::cout << "foo_builder constructor\n";
}
operator foo();
};
struct foo_promise {
foo_builder get_return_object() {
return{};
}
void return_value(int value) {
std::cout << "co_return with " << value << "\n";
}
std::experimental::suspend_never initial_suspend() {
std::cout << "Initial suspend\n";
return{};
}
std::experimental::suspend_never final_suspend() {
return{};
}
};
struct foo {
foo() {
std::cout << "foo constructor\n";
}
using promise_type = foo_promise;
};
foo_builder::operator foo() {
std::cout << "foo_builder conversion to foo\n";
return{};
}
foo coroutine() {
co_return 5;
}
foo simple() {
foo_promise p;
auto gro = p.get_return_object();
// co_await p.initial_suspend(); // initial suspend point
// co_return 5;
p.return_value(5); //S;
goto final_suspend;
final_suspend:
// co_await p.final_suspend(); // final suspend point
return gro;
}
int main() {
auto v = coroutine();
std::cout << "\nregular function:\n";
auto v2 = simple();
std::cin.ignore();
}
Output:
Initial suspend
foo_builder constructor
foo_builder conversion to foo
foo constructor
co_return with 5
regular function:
foo_builder constructor
co_return with 5
foo_builder conversion to foo
foo constructor
Upvotes: 1
Views: 1077
Reputation: 1209
Capturing return value of get_return_object()
by auto gro
was added in p0057r5. In previous revision (p0057r4) this part reads as
...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
}
and
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.
VS2015 obviously implements older version of this paper.
Upvotes: 2