forked from OSchip/llvm-project
[coroutines] Add diagnostics for copy/move assignment operators and functions with deduced return types.
Summary: The title says it all. Additionally this patch refactors the diagnostic code into a separate function. Reviewers: GorNishanov, rsmith Subscribers: majnemer, mehdi_amini, cfe-commits Differential Revision: https://reviews.llvm.org/D25292 llvm-svn: 285331
This commit is contained in:
parent
430b3e4893
commit
c8efda7f80
|
@ -8628,14 +8628,11 @@ def err_coroutine_unevaluated_context : Error<
|
|||
"'%0' cannot be used in an unevaluated context">;
|
||||
def err_coroutine_outside_function : Error<
|
||||
"'%0' cannot be used outside a function">;
|
||||
def err_coroutine_ctor_dtor : Error<
|
||||
"'%1' cannot be used in a %select{constructor|destructor}0">;
|
||||
def err_coroutine_constexpr : Error<
|
||||
"'%0' cannot be used in a constexpr function">;
|
||||
def err_coroutine_main : Error<
|
||||
"'main' cannot be a coroutine">;
|
||||
def err_coroutine_varargs : Error<
|
||||
"'%0' cannot be used in a varargs function">;
|
||||
def err_coroutine_invalid_func_context : Error<
|
||||
"'%1' cannot be used in %select{a constructor|a destructor"
|
||||
"|a copy assignment operator|a move assignment operator|the 'main' function"
|
||||
"|a constexpr function|a function with a deduced return type"
|
||||
"|a varargs function}0">;
|
||||
def ext_coroutine_without_co_await_co_yield : ExtWarn<
|
||||
"'co_return' used in a function "
|
||||
"that uses neither 'co_await' nor 'co_yield'">,
|
||||
|
|
|
@ -99,67 +99,98 @@ static QualType lookupPromiseType(Sema &S, const FunctionProtoType *FnType,
|
|||
return PromiseType;
|
||||
}
|
||||
|
||||
/// Check that this is a context in which a coroutine suspension can appear.
|
||||
static FunctionScopeInfo *
|
||||
checkCoroutineContext(Sema &S, SourceLocation Loc, StringRef Keyword) {
|
||||
static bool isValidCoroutineContext(Sema &S, SourceLocation Loc,
|
||||
StringRef Keyword) {
|
||||
// 'co_await' and 'co_yield' are not permitted in unevaluated operands.
|
||||
if (S.isUnevaluatedContext()) {
|
||||
S.Diag(Loc, diag::err_coroutine_unevaluated_context) << Keyword;
|
||||
return nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Any other usage must be within a function.
|
||||
// FIXME: Reject a coroutine with a deduced return type.
|
||||
auto *FD = dyn_cast<FunctionDecl>(S.CurContext);
|
||||
if (!FD) {
|
||||
S.Diag(Loc, isa<ObjCMethodDecl>(S.CurContext)
|
||||
? diag::err_coroutine_objc_method
|
||||
: diag::err_coroutine_outside_function) << Keyword;
|
||||
} else if (isa<CXXConstructorDecl>(FD) || isa<CXXDestructorDecl>(FD)) {
|
||||
// Coroutines TS [special]/6:
|
||||
// A special member function shall not be a coroutine.
|
||||
//
|
||||
// FIXME: We assume that this really means that a coroutine cannot
|
||||
// be a constructor or destructor.
|
||||
S.Diag(Loc, diag::err_coroutine_ctor_dtor)
|
||||
<< isa<CXXDestructorDecl>(FD) << Keyword;
|
||||
} else if (FD->isConstexpr()) {
|
||||
S.Diag(Loc, diag::err_coroutine_constexpr) << Keyword;
|
||||
} else if (FD->isVariadic()) {
|
||||
S.Diag(Loc, diag::err_coroutine_varargs) << Keyword;
|
||||
} else if (FD->isMain()) {
|
||||
S.Diag(FD->getLocStart(), diag::err_coroutine_main);
|
||||
S.Diag(Loc, diag::note_declared_coroutine_here)
|
||||
<< (Keyword == "co_await" ? 0 :
|
||||
Keyword == "co_yield" ? 1 : 2);
|
||||
} else {
|
||||
auto *ScopeInfo = S.getCurFunction();
|
||||
assert(ScopeInfo && "missing function scope for function");
|
||||
|
||||
// If we don't have a promise variable, build one now.
|
||||
if (!ScopeInfo->CoroutinePromise) {
|
||||
QualType T =
|
||||
FD->getType()->isDependentType()
|
||||
? S.Context.DependentTy
|
||||
: lookupPromiseType(S, FD->getType()->castAs<FunctionProtoType>(),
|
||||
Loc);
|
||||
if (T.isNull())
|
||||
return nullptr;
|
||||
|
||||
// Create and default-initialize the promise.
|
||||
ScopeInfo->CoroutinePromise =
|
||||
VarDecl::Create(S.Context, FD, FD->getLocation(), FD->getLocation(),
|
||||
&S.PP.getIdentifierTable().get("__promise"), T,
|
||||
S.Context.getTrivialTypeSourceInfo(T, Loc), SC_None);
|
||||
S.CheckVariableDeclarationType(ScopeInfo->CoroutinePromise);
|
||||
if (!ScopeInfo->CoroutinePromise->isInvalidDecl())
|
||||
S.ActOnUninitializedDecl(ScopeInfo->CoroutinePromise, false);
|
||||
}
|
||||
|
||||
return ScopeInfo;
|
||||
return false;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
// An enumeration for mapping the diagnostic type to the correct diagnostic
|
||||
// selection index.
|
||||
enum InvalidFuncDiag {
|
||||
DiagCtor = 0,
|
||||
DiagDtor,
|
||||
DiagCopyAssign,
|
||||
DiagMoveAssign,
|
||||
DiagMain,
|
||||
DiagConstexpr,
|
||||
DiagAutoRet,
|
||||
DiagVarargs,
|
||||
};
|
||||
bool Diagnosed = false;
|
||||
auto DiagInvalid = [&](InvalidFuncDiag ID) {
|
||||
S.Diag(Loc, diag::err_coroutine_invalid_func_context) << ID << Keyword;
|
||||
Diagnosed = true;
|
||||
return false;
|
||||
};
|
||||
|
||||
// Diagnose when a constructor, destructor, copy/move assignment operator,
|
||||
// or the function 'main' are declared as a coroutine.
|
||||
auto *MD = dyn_cast<CXXMethodDecl>(FD);
|
||||
if (MD && isa<CXXConstructorDecl>(MD))
|
||||
return DiagInvalid(DiagCtor);
|
||||
else if (MD && isa<CXXDestructorDecl>(MD))
|
||||
return DiagInvalid(DiagDtor);
|
||||
else if (MD && MD->isCopyAssignmentOperator())
|
||||
return DiagInvalid(DiagCopyAssign);
|
||||
else if (MD && MD->isMoveAssignmentOperator())
|
||||
return DiagInvalid(DiagMoveAssign);
|
||||
else if (FD->isMain())
|
||||
return DiagInvalid(DiagMain);
|
||||
|
||||
// Emit a diagnostics for each of the following conditions which is not met.
|
||||
if (FD->isConstexpr())
|
||||
DiagInvalid(DiagConstexpr);
|
||||
if (FD->getReturnType()->isUndeducedType())
|
||||
DiagInvalid(DiagAutoRet);
|
||||
if (FD->isVariadic())
|
||||
DiagInvalid(DiagVarargs);
|
||||
|
||||
return !Diagnosed;
|
||||
}
|
||||
|
||||
/// Check that this is a context in which a coroutine suspension can appear.
|
||||
static FunctionScopeInfo *checkCoroutineContext(Sema &S, SourceLocation Loc,
|
||||
StringRef Keyword) {
|
||||
if (!isValidCoroutineContext(S, Loc, Keyword))
|
||||
return nullptr;
|
||||
|
||||
assert(isa<FunctionDecl>(S.CurContext) && "not in a function scope");
|
||||
auto *FD = cast<FunctionDecl>(S.CurContext);
|
||||
auto *ScopeInfo = S.getCurFunction();
|
||||
assert(ScopeInfo && "missing function scope for function");
|
||||
|
||||
// If we don't have a promise variable, build one now.
|
||||
if (!ScopeInfo->CoroutinePromise) {
|
||||
QualType T = FD->getType()->isDependentType()
|
||||
? S.Context.DependentTy
|
||||
: lookupPromiseType(
|
||||
S, FD->getType()->castAs<FunctionProtoType>(), Loc);
|
||||
if (T.isNull())
|
||||
return nullptr;
|
||||
|
||||
// Create and default-initialize the promise.
|
||||
ScopeInfo->CoroutinePromise =
|
||||
VarDecl::Create(S.Context, FD, FD->getLocation(), FD->getLocation(),
|
||||
&S.PP.getIdentifierTable().get("__promise"), T,
|
||||
S.Context.getTrivialTypeSourceInfo(T, Loc), SC_None);
|
||||
S.CheckVariableDeclarationType(ScopeInfo->CoroutinePromise);
|
||||
if (!ScopeInfo->CoroutinePromise->isInvalidDecl())
|
||||
S.ActOnUninitializedDecl(ScopeInfo->CoroutinePromise, false);
|
||||
}
|
||||
|
||||
return ScopeInfo;
|
||||
}
|
||||
|
||||
static Expr *buildBuiltinCall(Sema &S, SourceLocation Loc, Builtin::ID Id,
|
||||
|
|
|
@ -178,7 +178,19 @@ struct CtorDtor {
|
|||
}
|
||||
// FIXME: The spec says this is ill-formed.
|
||||
void operator=(CtorDtor&) {
|
||||
co_yield 0;
|
||||
co_yield 0; // expected-error {{'co_yield' cannot be used in a copy assignment operator}}
|
||||
}
|
||||
void operator=(CtorDtor const &) {
|
||||
co_yield 0; // expected-error {{'co_yield' cannot be used in a copy assignment operator}}
|
||||
}
|
||||
void operator=(CtorDtor &&) {
|
||||
co_await a; // expected-error {{'co_await' cannot be used in a move assignment operator}}
|
||||
}
|
||||
void operator=(CtorDtor const &&) {
|
||||
co_await a; // expected-error {{'co_await' cannot be used in a move assignment operator}}
|
||||
}
|
||||
void operator=(int) {
|
||||
co_await a; // OK. Not a special member
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -191,14 +203,19 @@ void unevaluated() {
|
|||
typeid(co_yield a); // expected-error {{cannot be used in an unevaluated context}}
|
||||
}
|
||||
|
||||
constexpr void constexpr_coroutine() {
|
||||
constexpr auto constexpr_deduced_return_coroutine() {
|
||||
co_yield 0; // expected-error {{'co_yield' cannot be used in a constexpr function}}
|
||||
// expected-error@-1 {{'co_yield' cannot be used in a function with a deduced return type}}
|
||||
}
|
||||
|
||||
void varargs_coroutine(const char *, ...) {
|
||||
co_await a; // expected-error {{'co_await' cannot be used in a varargs function}}
|
||||
}
|
||||
|
||||
auto deduced_return_coroutine() {
|
||||
co_await a; // expected-error {{'co_await' cannot be used in a function with a deduced return type}}
|
||||
}
|
||||
|
||||
struct outer {};
|
||||
|
||||
namespace dependent_operator_co_await_lookup {
|
||||
|
@ -355,6 +372,6 @@ coro<bad_promise_8> calls_set_exception() {
|
|||
template<> struct std::experimental::coroutine_traits<int, int, const char**>
|
||||
{ using promise_type = promise; };
|
||||
|
||||
int main(int, const char**) { // expected-error {{'main' cannot be a coroutine}}
|
||||
co_await a; // expected-note {{function is a coroutine due to use of 'co_await' here}}
|
||||
int main(int, const char**) {
|
||||
co_await a; // expected-error {{'co_await' cannot be used in the 'main' function}}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue