-
Notifications
You must be signed in to change notification settings - Fork 10.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[coroutines] Fix co_await for range statement
Summary: Currently we build the co_await expressions on the wrong implicit statements of the implicit ranged for; Specifically we build the co_await expression wrapping the range declaration, but it should wrap the begin expression. This patch fixes co_await on range for. Reviewers: rsmith, GorNishanov Reviewed By: GorNishanov Subscribers: cfe-commits Differential Revision: https://reviews.llvm.org/D34021 llvm-svn: 305363
- Loading branch information
Showing
4 changed files
with
217 additions
and
37 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
// RUN: %clang_cc1 -triple x86_64-apple-darwin9 %s -std=c++14 -fcoroutines-ts \ | ||
// RUN: -fsyntax-only -Wignored-qualifiers -Wno-error=return-type -verify \ | ||
// RUN: -fblocks | ||
#include "Inputs/std-coroutine.h" | ||
|
||
using namespace std::experimental; | ||
|
||
|
||
template <class Begin> | ||
struct Awaiter { | ||
bool await_ready(); | ||
void await_suspend(coroutine_handle<>); | ||
Begin await_resume(); | ||
}; | ||
|
||
template <class Iter> struct BeginTag { BeginTag() = delete; }; | ||
template <class Iter> struct IncTag { IncTag() = delete; }; | ||
|
||
template <class Iter, bool Delete = false> | ||
struct CoawaitTag { CoawaitTag() = delete; }; | ||
|
||
template <class T> | ||
struct Iter { | ||
using value_type = T; | ||
using reference = T &; | ||
using pointer = T *; | ||
|
||
IncTag<Iter> operator++(); | ||
reference operator*(); | ||
pointer operator->(); | ||
}; | ||
template <class T> bool operator==(Iter<T>, Iter<T>); | ||
template <class T> bool operator!=(Iter<T>, Iter<T>); | ||
|
||
template <class T> | ||
struct Range { | ||
BeginTag<Iter<T>> begin(); | ||
Iter<T> end(); | ||
}; | ||
|
||
struct MyForLoopArrayAwaiter { | ||
struct promise_type { | ||
MyForLoopArrayAwaiter get_return_object() { return {}; } | ||
void return_void(); | ||
void unhandled_exception(); | ||
suspend_never initial_suspend(); | ||
suspend_never final_suspend(); | ||
template <class T> | ||
Awaiter<T *> await_transform(T *) = delete; // expected-note {{explicitly deleted}} | ||
}; | ||
}; | ||
MyForLoopArrayAwaiter g() { | ||
int arr[10] = {0}; | ||
for co_await(auto i : arr) {} | ||
// expected-error@-1 {{call to deleted member function 'await_transform'}} | ||
// expected-note@-2 {{'await_transform' implicitly required by 'co_await' here}} | ||
} | ||
|
||
struct ForLoopAwaiterBadBeginTransform { | ||
struct promise_type { | ||
ForLoopAwaiterBadBeginTransform get_return_object(); | ||
void return_void(); | ||
void unhandled_exception(); | ||
suspend_never initial_suspend(); | ||
suspend_never final_suspend(); | ||
|
||
template <class T> | ||
Awaiter<T> await_transform(BeginTag<T>) = delete; // expected-note 1+ {{explicitly deleted}} | ||
|
||
template <class T> | ||
CoawaitTag<T> await_transform(IncTag<T>); // expected-note 1+ {{candidate}} | ||
}; | ||
}; | ||
ForLoopAwaiterBadBeginTransform bad_begin() { | ||
Range<int> R; | ||
for co_await(auto i : R) {} | ||
// expected-error@-1 {{call to deleted member function 'await_transform'}} | ||
// expected-note@-2 {{'await_transform' implicitly required by 'co_await' here}} | ||
} | ||
template <class Dummy> | ||
ForLoopAwaiterBadBeginTransform bad_begin_template(Dummy) { | ||
Range<Dummy> R; | ||
for co_await(auto i : R) {} | ||
// expected-error@-1 {{call to deleted member function 'await_transform'}} | ||
// expected-note@-2 {{'await_transform' implicitly required by 'co_await' here}} | ||
} | ||
template ForLoopAwaiterBadBeginTransform bad_begin_template(int); // expected-note {{requested here}} | ||
|
||
template <class Iter> | ||
Awaiter<Iter> operator co_await(CoawaitTag<Iter, true>) = delete; | ||
// expected-note@-1 1+ {{explicitly deleted}} | ||
|
||
struct ForLoopAwaiterBadIncTransform { | ||
struct promise_type { | ||
ForLoopAwaiterBadIncTransform get_return_object(); | ||
void return_void(); | ||
void unhandled_exception(); | ||
suspend_never initial_suspend(); | ||
suspend_never final_suspend(); | ||
|
||
template <class T> | ||
Awaiter<T> await_transform(BeginTag<T> e); | ||
|
||
template <class T> | ||
CoawaitTag<T, true> await_transform(IncTag<T>); | ||
}; | ||
}; | ||
ForLoopAwaiterBadIncTransform bad_inc_transform() { | ||
Range<float> R; | ||
for co_await(auto i : R) {} | ||
// expected-error@-1 {{overload resolution selected deleted operator 'co_await'}} | ||
// expected-note@-2 {{in implicit call to 'operator++' for iterator of type 'Range<float>'}} | ||
} | ||
|
||
template <class Dummy> | ||
ForLoopAwaiterBadIncTransform bad_inc_transform_template(Dummy) { | ||
Range<Dummy> R; | ||
for co_await(auto i : R) {} | ||
// expected-error@-1 {{overload resolution selected deleted operator 'co_await'}} | ||
// expected-note@-2 {{in implicit call to 'operator++' for iterator of type 'Range<long>'}} | ||
} | ||
template ForLoopAwaiterBadIncTransform bad_inc_transform_template(long); // expected-note {{requested here}} | ||
|
||
// Ensure we mark and check the function as a coroutine even if it's | ||
// never instantiated. | ||
template <class T> | ||
constexpr void never_instant(T) { | ||
static_assert(sizeof(T) != sizeof(T), "function should not be instantiated"); | ||
for co_await(auto i : foo(T{})) {} | ||
// expected-error@-1 {{'co_await' cannot be used in a constexpr function}} | ||
} | ||
|
||
namespace NS { | ||
struct ForLoopAwaiterCoawaitLookup { | ||
struct promise_type { | ||
ForLoopAwaiterCoawaitLookup get_return_object(); | ||
void return_void(); | ||
void unhandled_exception(); | ||
suspend_never initial_suspend(); | ||
suspend_never final_suspend(); | ||
template <class T> | ||
CoawaitTag<T, false> await_transform(BeginTag<T> e); | ||
template <class T> | ||
Awaiter<T> await_transform(IncTag<T>); | ||
}; | ||
}; | ||
} // namespace NS | ||
using NS::ForLoopAwaiterCoawaitLookup; | ||
|
||
template <class T> | ||
ForLoopAwaiterCoawaitLookup test_coawait_lookup(T) { | ||
Range<T> R; | ||
for co_await(auto i : R) {} | ||
// expected-error@-1 {{no member named 'await_ready' in 'CoawaitTag<Iter<int>, false>'}} | ||
} | ||
template ForLoopAwaiterCoawaitLookup test_coawait_lookup(int); // expected-note {{requested here}} | ||
|
||
// FIXME: This test should fail as well since the newly declared operator co_await | ||
// should not be found by lookup. | ||
namespace NS2 { | ||
template <class Iter> | ||
Awaiter<Iter> operator co_await(CoawaitTag<Iter, false>); | ||
} | ||
using NS2::operator co_await; | ||
template ForLoopAwaiterCoawaitLookup test_coawait_lookup(long); |