Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -8364,6 +8364,8 @@ //===--------------------------------------------------------------------===// // C++ Coroutines TS // + bool ActOnCoroutineBodyStart(Scope *S, SourceLocation KwLoc, + StringRef Keyword); ExprResult ActOnCoawaitExpr(Scope *S, SourceLocation KwLoc, Expr *E); ExprResult ActOnCoyieldExpr(Scope *S, SourceLocation KwLoc, Expr *E); StmtResult ActOnCoreturnStmt(Scope *S, SourceLocation KwLoc, Expr *E); Index: lib/Sema/SemaCoroutine.cpp =================================================================== --- lib/Sema/SemaCoroutine.cpp +++ lib/Sema/SemaCoroutine.cpp @@ -470,11 +470,11 @@ return ScopeInfo; } -static bool actOnCoroutineBodyStart(Sema &S, Scope *SC, SourceLocation KWLoc, - StringRef Keyword) { - if (!checkCoroutineContext(S, KWLoc, Keyword)) +bool Sema::ActOnCoroutineBodyStart(Scope *SC, SourceLocation KWLoc, + StringRef Keyword) { + if (!checkCoroutineContext(*this, KWLoc, Keyword)) return false; - auto *ScopeInfo = S.getCurFunction(); + auto *ScopeInfo = getCurFunction(); assert(ScopeInfo->CoroutinePromise); // If we have existing coroutine statements then we have already built @@ -484,24 +484,24 @@ ScopeInfo->setNeedsCoroutineSuspends(false); - auto *Fn = cast(S.CurContext); + auto *Fn = cast(CurContext); SourceLocation Loc = Fn->getLocation(); // Build the initial suspend point auto buildSuspends = [&](StringRef Name) mutable -> StmtResult { ExprResult Suspend = - buildPromiseCall(S, ScopeInfo->CoroutinePromise, Loc, Name, None); + buildPromiseCall(*this, ScopeInfo->CoroutinePromise, Loc, Name, None); if (Suspend.isInvalid()) return StmtError(); - Suspend = buildOperatorCoawaitCall(S, SC, Loc, Suspend.get()); + Suspend = buildOperatorCoawaitCall(*this, SC, Loc, Suspend.get()); if (Suspend.isInvalid()) return StmtError(); - Suspend = S.BuildResolvedCoawaitExpr(Loc, Suspend.get(), - /*IsImplicit*/ true); - Suspend = S.ActOnFinishFullExpr(Suspend.get()); + Suspend = BuildResolvedCoawaitExpr(Loc, Suspend.get(), + /*IsImplicit*/ true); + Suspend = ActOnFinishFullExpr(Suspend.get()); if (Suspend.isInvalid()) { - S.Diag(Loc, diag::note_coroutine_promise_suspend_implicitly_required) + Diag(Loc, diag::note_coroutine_promise_suspend_implicitly_required) << ((Name == "initial_suspend") ? 0 : 1); - S.Diag(KWLoc, diag::note_declared_coroutine_here) << Keyword; + Diag(KWLoc, diag::note_declared_coroutine_here) << Keyword; return StmtError(); } return cast(Suspend.get()); @@ -521,7 +521,7 @@ } ExprResult Sema::ActOnCoawaitExpr(Scope *S, SourceLocation Loc, Expr *E) { - if (!actOnCoroutineBodyStart(*this, S, Loc, "co_await")) { + if (!ActOnCoroutineBodyStart(S, Loc, "co_await")) { CorrectDelayedTyposInExpr(E); return ExprError(); } @@ -613,7 +613,7 @@ } ExprResult Sema::ActOnCoyieldExpr(Scope *S, SourceLocation Loc, Expr *E) { - if (!actOnCoroutineBodyStart(*this, S, Loc, "co_yield")) { + if (!ActOnCoroutineBodyStart(S, Loc, "co_yield")) { CorrectDelayedTyposInExpr(E); return ExprError(); } @@ -665,7 +665,7 @@ } StmtResult Sema::ActOnCoreturnStmt(Scope *S, SourceLocation Loc, Expr *E) { - if (!actOnCoroutineBodyStart(*this, S, Loc, "co_return")) { + if (!ActOnCoroutineBodyStart(S, Loc, "co_return")) { CorrectDelayedTyposInExpr(E); return StmtError(); } Index: lib/Sema/SemaStmt.cpp =================================================================== --- lib/Sema/SemaStmt.cpp +++ lib/Sema/SemaStmt.cpp @@ -1989,11 +1989,11 @@ return StmtError(); } - // Coroutines: 'for co_await' implicitly co_awaits its range. - if (CoawaitLoc.isValid()) { - ExprResult Coawait = ActOnCoawaitExpr(S, CoawaitLoc, Range); - if (Coawait.isInvalid()) return StmtError(); - Range = Coawait.get(); + // Build the coroutine state immediately and not later during template + // instantiation + if (!CoawaitLoc.isInvalid()) { + if (!ActOnCoroutineBodyStart(S, CoawaitLoc, "co_await")) + return StmtError(); } // Build auto && __range = range-init @@ -2031,16 +2031,12 @@ /// BeginExpr and EndExpr are set and FRS_Success is returned on success; /// CandidateSet and BEF are set and some non-success value is returned on /// failure. -static Sema::ForRangeStatus BuildNonArrayForRange(Sema &SemaRef, - Expr *BeginRange, Expr *EndRange, - QualType RangeType, - VarDecl *BeginVar, - VarDecl *EndVar, - SourceLocation ColonLoc, - OverloadCandidateSet *CandidateSet, - ExprResult *BeginExpr, - ExprResult *EndExpr, - BeginEndFunction *BEF) { +static Sema::ForRangeStatus +BuildNonArrayForRange(Sema &SemaRef, Expr *BeginRange, Expr *EndRange, + QualType RangeType, VarDecl *BeginVar, VarDecl *EndVar, + SourceLocation ColonLoc, SourceLocation CoawaitLoc, + OverloadCandidateSet *CandidateSet, ExprResult *BeginExpr, + ExprResult *EndExpr, BeginEndFunction *BEF) { DeclarationNameInfo BeginNameInfo( &SemaRef.PP.getIdentifierTable().get("begin"), ColonLoc); DeclarationNameInfo EndNameInfo(&SemaRef.PP.getIdentifierTable().get("end"), @@ -2087,6 +2083,15 @@ << ColonLoc << BEF_begin << BeginRange->getType(); return RangeStatus; } + if (!CoawaitLoc.isInvalid()) { + // FIXME: getCurScope() should not be used during template instantiation. + // We should pick up the set of unqualified lookup results for operator + // co_await during the initial parse. + *BeginExpr = SemaRef.ActOnCoawaitExpr(SemaRef.getCurScope(), ColonLoc, + BeginExpr->get()); + if (BeginExpr->isInvalid()) + return Sema::FRS_DiagnosticIssued; + } if (FinishForRangeVarDecl(SemaRef, BeginVar, BeginExpr->get(), ColonLoc, diag::err_for_range_iter_deduction_failure)) { NoteForRangeBeginEndFunction(SemaRef, BeginExpr->get(), *BEF); @@ -2249,6 +2254,11 @@ // begin-expr is __range. BeginExpr = BeginRangeRef; + if (!CoawaitLoc.isInvalid()) { + BeginExpr = ActOnCoawaitExpr(S, ColonLoc, BeginExpr.get()); + if (BeginExpr.isInvalid()) + return StmtError(); + } if (FinishForRangeVarDecl(*this, BeginVar, BeginRangeRef.get(), ColonLoc, diag::err_for_range_iter_deduction_failure)) { NoteForRangeBeginEndFunction(*this, BeginExpr.get(), BEF_begin); @@ -2331,11 +2341,10 @@ OverloadCandidateSet CandidateSet(RangeLoc, OverloadCandidateSet::CSK_Normal); BeginEndFunction BEFFailure; - ForRangeStatus RangeStatus = - BuildNonArrayForRange(*this, BeginRangeRef.get(), - EndRangeRef.get(), RangeType, - BeginVar, EndVar, ColonLoc, &CandidateSet, - &BeginExpr, &EndExpr, &BEFFailure); + ForRangeStatus RangeStatus = BuildNonArrayForRange( + *this, BeginRangeRef.get(), EndRangeRef.get(), RangeType, BeginVar, + EndVar, ColonLoc, CoawaitLoc, &CandidateSet, &BeginExpr, &EndExpr, + &BEFFailure); if (Kind == BFRK_Build && RangeStatus == FRS_NoViableFunction && BEFFailure == BEF_begin) { @@ -2432,6 +2441,9 @@ IncrExpr = ActOnUnaryOp(S, ColonLoc, tok::plusplus, BeginRef.get()); if (!IncrExpr.isInvalid() && CoawaitLoc.isValid()) + // FIXME: getCurScope() should not be used during template instantiation. + // We should pick up the set of unqualified lookup results for operator + // co_await during the initial parse. IncrExpr = ActOnCoawaitExpr(S, CoawaitLoc, IncrExpr.get()); if (!IncrExpr.isInvalid()) IncrExpr = ActOnFinishFullExpr(IncrExpr.get()); Index: test/SemaCXX/coawait_range_for.cpp =================================================================== --- /dev/null +++ test/SemaCXX/coawait_range_for.cpp @@ -0,0 +1,187 @@ +// 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 +struct EnableIf {}; +template +struct EnableIf { using type = T; }; +template +using EnableIfT = typename EnableIf::type; + +template +struct Awaiter { + Awaiter(Begin b); + bool await_ready(); + void await_suspend(coroutine_handle<>); + Begin await_resume(); +}; + +template +struct BeginTag { + BeginTag() = delete; +}; + +template +struct IncTag { + IncTag() = delete; +}; + +template +struct CoawaitTag { + CoawaitTag() = delete; +}; + +template +struct MyIter { + using value_type = T; + using reference = T &; + using pointer = T *; + + IncTag operator++(); + reference operator*(); + pointer operator->(); +}; +template +bool operator==(MyIter, MyIter); +template +bool operator!=(MyIter, MyIter); + +template +struct MyRange { + BeginTag> begin(); + MyIter 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 + Awaiter 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 + Awaiter await_transform(BeginTag) = delete; // expected-note 1+ {{explicitly deleted}} + + template + CoawaitTag await_transform(IncTag); // expected-note 1+ {{candidate}} + }; +}; +ForLoopAwaiterBadBeginTransform bad_begin() { + MyRange 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(Dummy) { + MyRange 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 +Awaiter operator co_await(CoawaitTag) = 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 + Awaiter await_transform(BeginTag e); + + template + CoawaitTag await_transform(IncTag); + }; +}; +ForLoopAwaiterBadIncTransform bad_inc_transform() { + MyRange 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 'MyRange'}} + ((void)i); + } +} + +template +ForLoopAwaiterBadIncTransform bad_inc_transform_template(Dummy) { + MyRange 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 'MyRange'}} + ((void)i); + } +} +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 +constexpr void never_instant(T) { + for co_await(auto i : foo(T{})) {} + // expected-error@-1 {{'co_await' cannot be used in a constexpr function}} +} + +struct ForLoopAwaiterCoawaitLookup { + struct promise_type { + ForLoopAwaiterCoawaitLookup get_return_object(); + void return_void(); + void unhandled_exception(); + suspend_never initial_suspend(); + suspend_never final_suspend(); + template + CoawaitTag await_transform(BeginTag e); + template + Awaiter await_transform(IncTag); + }; +}; +template +ForLoopAwaiterCoawaitLookup test_coawait_lookup(T) { + MyRange R; + for co_await(auto i : R) {} + // expected-error@-1 {{no member named 'await_ready' in 'CoawaitTag, 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. +template +Awaiter operator co_await(CoawaitTag); +template ForLoopAwaiterCoawaitLookup test_coawait_lookup(long);