[coroutines] Fix rebuilding of implicit and dependent coroutine statements.

Summary:
Certain implicitly generated coroutine statements, such as the calls to 'return_value()' or `return_void()` or `get_return_object_on_allocation_failure()`, cannot be built until the promise type is no longer dependent. This means they are not built until after the coroutine body statement has been transformed.

This patch fixes an issue where these statements would never be built for coroutine templates.

It also fixes a small issue where diagnostics about `get_return_object_on_allocation_failure()` were incorrectly suppressed. 

Reviewers: rsmith, majnemer, GorNishanov, aaron.ballman

Reviewed By: GorNishanov

Subscribers: cfe-commits

Differential Revision: https://reviews.llvm.org/D31487

llvm-svn: 299380
This commit is contained in:
Eric Fiselier 2017-04-03 19:21:00 +00:00
parent dee5565869
commit bee782bb92
6 changed files with 232 additions and 115 deletions

View File

@ -344,6 +344,10 @@ private:
public: public:
static CoroutineBodyStmt *Create(const ASTContext &C, CtorArgs const &Args); static CoroutineBodyStmt *Create(const ASTContext &C, CtorArgs const &Args);
bool hasDependentPromiseType() const {
return getPromiseDecl()->getType()->isDependentType();
}
/// \brief Retrieve the body of the coroutine as written. This will be either /// \brief Retrieve the body of the coroutine as written. This will be either
/// a CompoundStmt or a TryStmt. /// a CompoundStmt or a TryStmt.
Stmt *getBody() const { Stmt *getBody() const {

View File

@ -112,4 +112,4 @@ CoroutineBodyStmt::CoroutineBodyStmt(CoroutineBodyStmt::CtorArgs const &Args)
Args.ReturnStmtOnAllocFailure; Args.ReturnStmtOnAllocFailure;
std::copy(Args.ParamMoves.begin(), Args.ParamMoves.end(), std::copy(Args.ParamMoves.begin(), Args.ParamMoves.end(),
const_cast<Stmt **>(getParamMoves().data())); const_cast<Stmt **>(getParamMoves().data()));
} }

View File

@ -0,0 +1,70 @@
//===- CoroutineStmtBuilder.h - Implicit coroutine stmt builder -*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//===----------------------------------------------------------------------===//
//
// This file defines CoroutineStmtBuilder, a class for building the implicit
// statements required for building a coroutine body.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_LIB_SEMA_COROUTINESTMTBUILDER_H
#define LLVM_CLANG_LIB_SEMA_COROUTINESTMTBUILDER_H
#include "clang/AST/Decl.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/StmtCXX.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Sema/SemaInternal.h"
namespace clang {
class CoroutineStmtBuilder : public CoroutineBodyStmt::CtorArgs {
Sema &S;
FunctionDecl &FD;
sema::FunctionScopeInfo &Fn;
bool IsValid = true;
SourceLocation Loc;
QualType RetType;
SmallVector<Stmt *, 4> ParamMovesVector;
const bool IsPromiseDependentType;
CXXRecordDecl *PromiseRecordDecl = nullptr;
public:
/// \brief Construct a CoroutineStmtBuilder and initialize the promise
/// statement and initial/final suspends from the FunctionScopeInfo.
CoroutineStmtBuilder(Sema &S, FunctionDecl &FD, sema::FunctionScopeInfo &Fn,
Stmt *Body);
/// \brief Build the coroutine body statements, including the
/// "promise dependent" statements when the promise type is not dependent.
bool buildStatements();
/// \brief Build the coroutine body statements that require a non-dependent
/// promise type in order to construct.
///
/// For example different new/delete overloads are selected depending on
/// if the promise type provides `unhandled_exception()`, and therefore they
/// cannot be built until the promise type is complete so that we can perform
/// name lookup.
bool buildDependentStatements();
bool isInvalid() const { return !this->IsValid; }
private:
bool makePromiseStmt();
bool makeInitialAndFinalSuspend();
bool makeNewAndDeleteExpr();
bool makeOnFallthrough();
bool makeOnException();
bool makeReturnObject();
bool makeReturnOnAllocFailure();
bool makeParamMoves();
};
} // end namespace clang
#endif // LLVM_CLANG_LIB_SEMA_COROUTINESTMTBUILDER_H

View File

@ -11,13 +11,15 @@
// //
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
#include "clang/Sema/SemaInternal.h" #include "CoroutineStmtBuilder.h"
#include "clang/AST/Decl.h" #include "clang/AST/Decl.h"
#include "clang/AST/ExprCXX.h" #include "clang/AST/ExprCXX.h"
#include "clang/AST/StmtCXX.h" #include "clang/AST/StmtCXX.h"
#include "clang/Lex/Preprocessor.h" #include "clang/Lex/Preprocessor.h"
#include "clang/Sema/Initialization.h" #include "clang/Sema/Initialization.h"
#include "clang/Sema/Overload.h" #include "clang/Sema/Overload.h"
#include "clang/Sema/SemaInternal.h"
using namespace clang; using namespace clang;
using namespace sema; using namespace sema;
@ -683,47 +685,6 @@ static FunctionDecl *findDeleteForPromise(Sema &S, SourceLocation Loc,
return OperatorDelete; return OperatorDelete;
} }
namespace {
class SubStmtBuilder : public CoroutineBodyStmt::CtorArgs {
Sema &S;
FunctionDecl &FD;
FunctionScopeInfo &Fn;
bool IsValid;
SourceLocation Loc;
QualType RetType;
SmallVector<Stmt *, 4> ParamMovesVector;
const bool IsPromiseDependentType;
CXXRecordDecl *PromiseRecordDecl = nullptr;
public:
SubStmtBuilder(Sema &S, FunctionDecl &FD, FunctionScopeInfo &Fn, Stmt *Body)
: S(S), FD(FD), Fn(Fn), Loc(FD.getLocation()),
IsPromiseDependentType(
!Fn.CoroutinePromise ||
Fn.CoroutinePromise->getType()->isDependentType()) {
this->Body = Body;
if (!IsPromiseDependentType) {
PromiseRecordDecl = Fn.CoroutinePromise->getType()->getAsCXXRecordDecl();
assert(PromiseRecordDecl && "Type should have already been checked");
}
this->IsValid = makePromiseStmt() && makeInitialAndFinalSuspend() &&
makeOnException() && makeOnFallthrough() &&
makeReturnOnAllocFailure() && makeNewAndDeleteExpr() &&
makeReturnObject() && makeParamMoves();
}
bool isInvalid() const { return !this->IsValid; }
bool makePromiseStmt();
bool makeInitialAndFinalSuspend();
bool makeNewAndDeleteExpr();
bool makeOnFallthrough();
bool makeOnException();
bool makeReturnObject();
bool makeReturnOnAllocFailure();
bool makeParamMoves();
};
}
void Sema::CheckCompletedCoroutineBody(FunctionDecl *FD, Stmt *&Body) { void Sema::CheckCompletedCoroutineBody(FunctionDecl *FD, Stmt *&Body) {
FunctionScopeInfo *Fn = getCurFunction(); FunctionScopeInfo *Fn = getCurFunction();
@ -750,15 +711,47 @@ void Sema::CheckCompletedCoroutineBody(FunctionDecl *FD, Stmt *&Body) {
Diag(Fn->FirstCoroutineStmtLoc, diag::note_declared_coroutine_here) Diag(Fn->FirstCoroutineStmtLoc, diag::note_declared_coroutine_here)
<< Fn->getFirstCoroutineStmtKeyword(); << Fn->getFirstCoroutineStmtKeyword();
} }
SubStmtBuilder Builder(*this, *FD, *Fn, Body); CoroutineStmtBuilder Builder(*this, *FD, *Fn, Body);
if (Builder.isInvalid()) if (Builder.isInvalid() || !Builder.buildStatements())
return FD->setInvalidDecl(); return FD->setInvalidDecl();
// Build body for the coroutine wrapper statement. // Build body for the coroutine wrapper statement.
Body = CoroutineBodyStmt::Create(Context, Builder); Body = CoroutineBodyStmt::Create(Context, Builder);
} }
bool SubStmtBuilder::makePromiseStmt() { CoroutineStmtBuilder::CoroutineStmtBuilder(Sema &S, FunctionDecl &FD,
sema::FunctionScopeInfo &Fn,
Stmt *Body)
: S(S), FD(FD), Fn(Fn), Loc(FD.getLocation()),
IsPromiseDependentType(
!Fn.CoroutinePromise ||
Fn.CoroutinePromise->getType()->isDependentType()) {
this->Body = Body;
if (!IsPromiseDependentType) {
PromiseRecordDecl = Fn.CoroutinePromise->getType()->getAsCXXRecordDecl();
assert(PromiseRecordDecl && "Type should have already been checked");
}
this->IsValid = makePromiseStmt() && makeInitialAndFinalSuspend();
}
bool CoroutineStmtBuilder::buildStatements() {
assert(this->IsValid && "coroutine already invalid");
this->IsValid = makeReturnObject() && makeParamMoves();
if (this->IsValid && !IsPromiseDependentType)
buildDependentStatements();
return this->IsValid;
}
bool CoroutineStmtBuilder::buildDependentStatements() {
assert(this->IsValid && "coroutine already invalid");
assert(!this->IsPromiseDependentType &&
"coroutine cannot have a dependent promise type");
this->IsValid = makeOnException() && makeOnFallthrough() &&
makeReturnOnAllocFailure() && makeNewAndDeleteExpr();
return this->IsValid;
}
bool CoroutineStmtBuilder::makePromiseStmt() {
// Form a declaration statement for the promise declaration, so that AST // Form a declaration statement for the promise declaration, so that AST
// visitors can more easily find it. // visitors can more easily find it.
StmtResult PromiseStmt = StmtResult PromiseStmt =
@ -770,7 +763,7 @@ bool SubStmtBuilder::makePromiseStmt() {
return true; return true;
} }
bool SubStmtBuilder::makeInitialAndFinalSuspend() { bool CoroutineStmtBuilder::makeInitialAndFinalSuspend() {
if (Fn.hasInvalidCoroutineSuspends()) if (Fn.hasInvalidCoroutineSuspends())
return false; return false;
this->InitialSuspend = cast<Expr>(Fn.CoroutineSuspends.first); this->InitialSuspend = cast<Expr>(Fn.CoroutineSuspends.first);
@ -801,8 +794,9 @@ static bool diagReturnOnAllocFailure(Sema &S, Expr *E,
return false; return false;
} }
bool SubStmtBuilder::makeReturnOnAllocFailure() { bool CoroutineStmtBuilder::makeReturnOnAllocFailure() {
if (!PromiseRecordDecl) return true; assert(!IsPromiseDependentType &&
"cannot make statement while the promise type is dependent");
// [dcl.fct.def.coroutine]/8 // [dcl.fct.def.coroutine]/8
// The unqualified-id get_return_object_on_allocation_failure is looked up in // The unqualified-id get_return_object_on_allocation_failure is looked up in
@ -813,22 +807,22 @@ bool SubStmtBuilder::makeReturnOnAllocFailure() {
DeclarationName DN = DeclarationName DN =
S.PP.getIdentifierInfo("get_return_object_on_allocation_failure"); S.PP.getIdentifierInfo("get_return_object_on_allocation_failure");
LookupResult Found(S, DN, Loc, Sema::LookupMemberName); LookupResult Found(S, DN, Loc, Sema::LookupMemberName);
// Suppress diagnostics when a private member is selected. The same warnings if (!S.LookupQualifiedName(Found, PromiseRecordDecl))
// will be produced again when building the call. return true;
Found.suppressDiagnostics();
if (!S.LookupQualifiedName(Found, PromiseRecordDecl)) return true;
CXXScopeSpec SS; CXXScopeSpec SS;
ExprResult DeclNameExpr = ExprResult DeclNameExpr =
S.BuildDeclarationNameExpr(SS, Found, /*NeedsADL=*/false); S.BuildDeclarationNameExpr(SS, Found, /*NeedsADL=*/false);
if (DeclNameExpr.isInvalid()) return false; if (DeclNameExpr.isInvalid())
return false;
if (!diagReturnOnAllocFailure(S, DeclNameExpr.get(), PromiseRecordDecl, Fn)) if (!diagReturnOnAllocFailure(S, DeclNameExpr.get(), PromiseRecordDecl, Fn))
return false; return false;
ExprResult ReturnObjectOnAllocationFailure = ExprResult ReturnObjectOnAllocationFailure =
S.ActOnCallExpr(nullptr, DeclNameExpr.get(), Loc, {}, Loc); S.ActOnCallExpr(nullptr, DeclNameExpr.get(), Loc, {}, Loc);
if (ReturnObjectOnAllocationFailure.isInvalid()) return false; if (ReturnObjectOnAllocationFailure.isInvalid())
return false;
// FIXME: ActOnReturnStmt expects a scope that is inside of the function, due // FIXME: ActOnReturnStmt expects a scope that is inside of the function, due
// to CheckJumpOutOfSEHFinally(*this, ReturnLoc, *CurScope->getFnParent()); // to CheckJumpOutOfSEHFinally(*this, ReturnLoc, *CurScope->getFnParent());
@ -837,17 +831,18 @@ bool SubStmtBuilder::makeReturnOnAllocFailure() {
// Use BuildReturnStmt here to unbreak sanitized tests. (Gor:3/27/2017) // Use BuildReturnStmt here to unbreak sanitized tests. (Gor:3/27/2017)
StmtResult ReturnStmt = StmtResult ReturnStmt =
S.BuildReturnStmt(Loc, ReturnObjectOnAllocationFailure.get()); S.BuildReturnStmt(Loc, ReturnObjectOnAllocationFailure.get());
if (ReturnStmt.isInvalid()) return false; if (ReturnStmt.isInvalid())
return false;
this->ReturnStmtOnAllocFailure = ReturnStmt.get(); this->ReturnStmtOnAllocFailure = ReturnStmt.get();
return true; return true;
} }
bool SubStmtBuilder::makeNewAndDeleteExpr() { bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
// Form and check allocation and deallocation calls. // Form and check allocation and deallocation calls.
assert(!IsPromiseDependentType &&
"cannot make statement while the promise type is dependent");
QualType PromiseType = Fn.CoroutinePromise->getType(); QualType PromiseType = Fn.CoroutinePromise->getType();
if (PromiseType->isDependentType())
return true;
if (S.RequireCompleteType(Loc, PromiseType, diag::err_incomplete_type)) if (S.RequireCompleteType(Loc, PromiseType, diag::err_incomplete_type))
return false; return false;
@ -920,9 +915,9 @@ bool SubStmtBuilder::makeNewAndDeleteExpr() {
return true; return true;
} }
bool SubStmtBuilder::makeOnFallthrough() { bool CoroutineStmtBuilder::makeOnFallthrough() {
if (!PromiseRecordDecl) assert(!IsPromiseDependentType &&
return true; "cannot make statement while the promise type is dependent");
// [dcl.fct.def.coroutine]/4 // [dcl.fct.def.coroutine]/4
// The unqualified-ids 'return_void' and 'return_value' are looked up in // The unqualified-ids 'return_void' and 'return_value' are looked up in
@ -951,11 +946,10 @@ bool SubStmtBuilder::makeOnFallthrough() {
return true; return true;
} }
bool SubStmtBuilder::makeOnException() { bool CoroutineStmtBuilder::makeOnException() {
// Try to form 'p.unhandled_exception();' // Try to form 'p.unhandled_exception();'
assert(!IsPromiseDependentType &&
if (!PromiseRecordDecl) "cannot make statement while the promise type is dependent");
return true;
const bool RequireUnhandledException = S.getLangOpts().CXXExceptions; const bool RequireUnhandledException = S.getLangOpts().CXXExceptions;
@ -983,7 +977,7 @@ bool SubStmtBuilder::makeOnException() {
return true; return true;
} }
bool SubStmtBuilder::makeReturnObject() { bool CoroutineStmtBuilder::makeReturnObject() {
// Build implicit 'p.get_return_object()' expression and form initialization // Build implicit 'p.get_return_object()' expression and form initialization
// of return type from it. // of return type from it.
@ -1008,7 +1002,7 @@ bool SubStmtBuilder::makeReturnObject() {
return true; return true;
} }
bool SubStmtBuilder::makeParamMoves() { bool CoroutineStmtBuilder::makeParamMoves() {
// FIXME: Perform move-initialization of parameters into frame-local copies. // FIXME: Perform move-initialization of parameters into frame-local copies.
return true; return true;
} }

View File

@ -14,6 +14,7 @@
#ifndef LLVM_CLANG_LIB_SEMA_TREETRANSFORM_H #ifndef LLVM_CLANG_LIB_SEMA_TREETRANSFORM_H
#define LLVM_CLANG_LIB_SEMA_TREETRANSFORM_H #define LLVM_CLANG_LIB_SEMA_TREETRANSFORM_H
#include "CoroutineStmtBuilder.h"
#include "TypeLocBuilder.h" #include "TypeLocBuilder.h"
#include "clang/AST/Decl.h" #include "clang/AST/Decl.h"
#include "clang/AST/DeclObjC.h" #include "clang/AST/DeclObjC.h"
@ -6851,13 +6852,9 @@ TreeTransform<Derived>::TransformMSAsmStmt(MSAsmStmt *S) {
template<typename Derived> template<typename Derived>
StmtResult StmtResult
TreeTransform<Derived>::TransformCoroutineBodyStmt(CoroutineBodyStmt *S) { TreeTransform<Derived>::TransformCoroutineBodyStmt(CoroutineBodyStmt *S) {
// The coroutine body should be re-formed by the caller if necessary.
// FIXME: The coroutine body is always rebuilt by ActOnFinishFunctionBody
CoroutineBodyStmt::CtorArgs BodyArgs;
auto *ScopeInfo = SemaRef.getCurFunction(); auto *ScopeInfo = SemaRef.getCurFunction();
auto *FD = cast<FunctionDecl>(SemaRef.CurContext); auto *FD = cast<FunctionDecl>(SemaRef.CurContext);
assert(ScopeInfo && !ScopeInfo->CoroutinePromise && assert(FD && ScopeInfo && !ScopeInfo->CoroutinePromise &&
ScopeInfo->NeedsCoroutineSuspends && ScopeInfo->NeedsCoroutineSuspends &&
ScopeInfo->CoroutineSuspends.first == nullptr && ScopeInfo->CoroutineSuspends.first == nullptr &&
ScopeInfo->CoroutineSuspends.second == nullptr && ScopeInfo->CoroutineSuspends.second == nullptr &&
@ -6869,17 +6866,11 @@ TreeTransform<Derived>::TransformCoroutineBodyStmt(CoroutineBodyStmt *S) {
// The new CoroutinePromise object needs to be built and put into the current // The new CoroutinePromise object needs to be built and put into the current
// FunctionScopeInfo before any transformations or rebuilding occurs. // FunctionScopeInfo before any transformations or rebuilding occurs.
auto *Promise = S->getPromiseDecl(); auto *Promise = SemaRef.buildCoroutinePromise(FD->getLocation());
auto *NewPromise = SemaRef.buildCoroutinePromise(FD->getLocation()); if (!Promise)
if (!NewPromise)
return StmtError(); return StmtError();
getDerived().transformedLocalDecl(Promise, NewPromise); getDerived().transformedLocalDecl(S->getPromiseDecl(), Promise);
ScopeInfo->CoroutinePromise = NewPromise; ScopeInfo->CoroutinePromise = Promise;
StmtResult PromiseStmt = SemaRef.ActOnDeclStmt(
SemaRef.ConvertDeclToDeclGroup(NewPromise),
FD->getLocation(), FD->getLocation());
assert(!PromiseStmt.isInvalid());
BodyArgs.Promise = PromiseStmt.get();
// Transform the implicit coroutine statements we built during the initial // Transform the implicit coroutine statements we built during the initial
// parse. // parse.
@ -6892,52 +6883,68 @@ TreeTransform<Derived>::TransformCoroutineBodyStmt(CoroutineBodyStmt *S) {
return StmtError(); return StmtError();
ScopeInfo->setCoroutineSuspends(InitSuspend.get(), FinalSuspend.get()); ScopeInfo->setCoroutineSuspends(InitSuspend.get(), FinalSuspend.get());
assert(isa<Expr>(InitSuspend.get()) && isa<Expr>(FinalSuspend.get())); assert(isa<Expr>(InitSuspend.get()) && isa<Expr>(FinalSuspend.get()));
BodyArgs.InitialSuspend = cast<Expr>(InitSuspend.get());
BodyArgs.FinalSuspend = cast<Expr>(FinalSuspend.get());
StmtResult BodyRes = getDerived().TransformStmt(S->getBody()); StmtResult BodyRes = getDerived().TransformStmt(S->getBody());
if (BodyRes.isInvalid()) if (BodyRes.isInvalid())
return StmtError(); return StmtError();
BodyArgs.Body = BodyRes.get();
if (S->getFallthroughHandler()) { CoroutineStmtBuilder Builder(SemaRef, *FD, *ScopeInfo, BodyRes.get());
StmtResult Res = getDerived().TransformStmt(S->getFallthroughHandler()); if (Builder.isInvalid())
if (Res.isInvalid()) return StmtError();
Expr *ReturnObject = S->getReturnValueInit();
assert(ReturnObject && "the return object is expected to be valid");
ExprResult Res = getDerived().TransformInitializer(ReturnObject,
/*NoCopyInit*/ false);
if (Res.isInvalid())
return StmtError();
Builder.ReturnValue = Res.get();
if (S->hasDependentPromiseType()) {
assert(!Promise->getType()->isDependentType() &&
"the promise type must no longer be dependent");
assert(!S->getFallthroughHandler() && !S->getExceptionHandler() &&
!S->getReturnStmtOnAllocFailure() && !S->getDeallocate() &&
"these nodes should not have been built yet");
if (!Builder.buildDependentStatements())
return StmtError(); return StmtError();
BodyArgs.OnFallthrough = Res.get(); } else {
} if (auto *OnFallthrough = S->getFallthroughHandler()) {
StmtResult Res = getDerived().TransformStmt(OnFallthrough);
if (Res.isInvalid())
return StmtError();
Builder.OnFallthrough = Res.get();
}
if (S->getExceptionHandler()) { if (auto *OnException = S->getExceptionHandler()) {
StmtResult Res = getDerived().TransformStmt(S->getExceptionHandler()); StmtResult Res = getDerived().TransformStmt(OnException);
if (Res.isInvalid()) if (Res.isInvalid())
return StmtError(); return StmtError();
BodyArgs.OnException = Res.get(); Builder.OnException = Res.get();
} }
// Transform any additional statements we may have already built if (auto *OnAllocFailure = S->getReturnStmtOnAllocFailure()) {
if (S->getAllocate() && S->getDeallocate()) { StmtResult Res = getDerived().TransformStmt(OnAllocFailure);
if (Res.isInvalid())
return StmtError();
Builder.ReturnStmtOnAllocFailure = Res.get();
}
// Transform any additional statements we may have already built
assert(S->getAllocate() && S->getDeallocate() &&
"allocation and deallocation calls must already be built");
ExprResult AllocRes = getDerived().TransformExpr(S->getAllocate()); ExprResult AllocRes = getDerived().TransformExpr(S->getAllocate());
if (AllocRes.isInvalid()) if (AllocRes.isInvalid())
return StmtError(); return StmtError();
BodyArgs.Allocate = AllocRes.get(); Builder.Allocate = AllocRes.get();
ExprResult DeallocRes = getDerived().TransformExpr(S->getDeallocate()); ExprResult DeallocRes = getDerived().TransformExpr(S->getDeallocate());
if (DeallocRes.isInvalid()) if (DeallocRes.isInvalid())
return StmtError(); return StmtError();
BodyArgs.Deallocate = DeallocRes.get(); Builder.Deallocate = DeallocRes.get();
} }
Expr *ReturnObject = S->getReturnValueInit(); return getDerived().RebuildCoroutineBodyStmt(Builder);
if (ReturnObject) {
ExprResult Res = getDerived().TransformInitializer(ReturnObject,
/*NoCopyInit*/false);
if (Res.isInvalid())
return StmtError();
BodyArgs.ReturnValue = Res.get();
}
// Do a partial rebuild of the coroutine body and stash it in the ScopeInfo
return getDerived().RebuildCoroutineBodyStmt(BodyArgs);
} }
template<typename Derived> template<typename Derived>

View File

@ -534,6 +534,12 @@ coro<bad_promise_6> bad_implicit_return() { // expected-error {{'bad_promise_6'
co_await a; co_await a;
} }
template <class T>
coro<T> bad_implicit_return_dependent(T) { // expected-error {{'bad_promise_6' declares both 'return_value' and 'return_void'}}
co_await a;
}
template coro<bad_promise_6> bad_implicit_return_dependent(bad_promise_6); // expected-note {{in instantiation}}
struct bad_promise_7 { struct bad_promise_7 {
coro<bad_promise_7> get_return_object(); coro<bad_promise_7> get_return_object();
suspend_always initial_suspend(); suspend_always initial_suspend();
@ -544,6 +550,12 @@ coro<bad_promise_7> no_unhandled_exception() { // expected-error {{'bad_promise_
co_await a; co_await a;
} }
template <class T>
coro<T> no_unhandled_exception_dependent(T) { // expected-error {{'bad_promise_7' is required to declare the member 'unhandled_exception()'}}
co_await a;
}
template coro<bad_promise_7> no_unhandled_exception_dependent(bad_promise_7); // expected-note {{in instantiation}}
struct bad_promise_base { struct bad_promise_base {
private: private:
void return_void(); void return_void();
@ -552,9 +564,9 @@ struct bad_promise_8 : bad_promise_base {
coro<bad_promise_8> get_return_object(); coro<bad_promise_8> get_return_object();
suspend_always initial_suspend(); suspend_always initial_suspend();
suspend_always final_suspend(); suspend_always final_suspend();
void unhandled_exception() __attribute__((unavailable)); // expected-note {{made unavailable}} void unhandled_exception() __attribute__((unavailable)); // expected-note 2 {{made unavailable}}
void unhandled_exception() const; // expected-note {{candidate}} void unhandled_exception() const; // expected-note 2 {{candidate}}
void unhandled_exception(void *) const; // expected-note {{requires 1 argument, but 0 were provided}} void unhandled_exception(void *) const; // expected-note 2 {{requires 1 argument, but 0 were provided}}
}; };
coro<bad_promise_8> calls_unhandled_exception() { coro<bad_promise_8> calls_unhandled_exception() {
// expected-error@-1 {{call to unavailable member function 'unhandled_exception'}} // expected-error@-1 {{call to unavailable member function 'unhandled_exception'}}
@ -563,6 +575,13 @@ coro<bad_promise_8> calls_unhandled_exception() {
co_await a; co_await a;
} }
template <class T>
coro<T> calls_unhandled_exception_dependent(T) {
// expected-error@-1 {{call to unavailable member function 'unhandled_exception'}}
co_await a;
}
template coro<bad_promise_8> calls_unhandled_exception_dependent(bad_promise_8); // expected-note {{in instantiation}}
struct bad_promise_9 { struct bad_promise_9 {
coro<bad_promise_9> get_return_object(); coro<bad_promise_9> get_return_object();
suspend_always initial_suspend(); suspend_always initial_suspend();
@ -652,3 +671,26 @@ struct std::experimental::coroutine_traits<int, promise_on_alloc_failure_tag> {
extern "C" int f(promise_on_alloc_failure_tag) { extern "C" int f(promise_on_alloc_failure_tag) {
co_return; //expected-note {{function is a coroutine due to use of 'co_return' here}} co_return; //expected-note {{function is a coroutine due to use of 'co_return' here}}
} }
struct bad_promise_11 {
coro<bad_promise_11> get_return_object();
suspend_always initial_suspend();
suspend_always final_suspend();
void unhandled_exception();
void return_void();
private:
static coro<bad_promise_11> get_return_object_on_allocation_failure(); // expected-note 2 {{declared private here}}
};
coro<bad_promise_11> private_alloc_failure_handler() {
// expected-error@-1 {{'get_return_object_on_allocation_failure' is a private member of 'bad_promise_11'}}
co_return; // FIXME: Add a "declared coroutine here" note.
}
template <class T>
coro<T> dependent_private_alloc_failure_handler(T) {
// expected-error@-1 {{'get_return_object_on_allocation_failure' is a private member of 'bad_promise_11'}}
co_return; // FIXME: Add a "declared coroutine here" note.
}
template coro<bad_promise_11> dependent_private_alloc_failure_handler(bad_promise_11);
// expected-note@-1 {{requested here}}