Skip to content

Commit 038c96d

Browse files
committed
[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 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@285331 91177308-0d34-0410-b5e6-96231b3b80d8
1 parent 63ba1c9 commit 038c96d

File tree

3 files changed

+104
-59
lines changed

3 files changed

+104
-59
lines changed

include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8628,14 +8628,11 @@ def err_coroutine_unevaluated_context : Error<
86288628
"'%0' cannot be used in an unevaluated context">;
86298629
def err_coroutine_outside_function : Error<
86308630
"'%0' cannot be used outside a function">;
8631-
def err_coroutine_ctor_dtor : Error<
8632-
"'%1' cannot be used in a %select{constructor|destructor}0">;
8633-
def err_coroutine_constexpr : Error<
8634-
"'%0' cannot be used in a constexpr function">;
8635-
def err_coroutine_main : Error<
8636-
"'main' cannot be a coroutine">;
8637-
def err_coroutine_varargs : Error<
8638-
"'%0' cannot be used in a varargs function">;
8631+
def err_coroutine_invalid_func_context : Error<
8632+
"'%1' cannot be used in %select{a constructor|a destructor"
8633+
"|a copy assignment operator|a move assignment operator|the 'main' function"
8634+
"|a constexpr function|a function with a deduced return type"
8635+
"|a varargs function}0">;
86398636
def ext_coroutine_without_co_await_co_yield : ExtWarn<
86408637
"'co_return' used in a function "
86418638
"that uses neither 'co_await' nor 'co_yield'">,

lib/Sema/SemaCoroutine.cpp

Lines changed: 78 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -99,67 +99,98 @@ static QualType lookupPromiseType(Sema &S, const FunctionProtoType *FnType,
9999
return PromiseType;
100100
}
101101

102-
/// Check that this is a context in which a coroutine suspension can appear.
103-
static FunctionScopeInfo *
104-
checkCoroutineContext(Sema &S, SourceLocation Loc, StringRef Keyword) {
102+
static bool isValidCoroutineContext(Sema &S, SourceLocation Loc,
103+
StringRef Keyword) {
105104
// 'co_await' and 'co_yield' are not permitted in unevaluated operands.
106105
if (S.isUnevaluatedContext()) {
107106
S.Diag(Loc, diag::err_coroutine_unevaluated_context) << Keyword;
108-
return nullptr;
107+
return false;
109108
}
110109

111110
// Any other usage must be within a function.
112-
// FIXME: Reject a coroutine with a deduced return type.
113111
auto *FD = dyn_cast<FunctionDecl>(S.CurContext);
114112
if (!FD) {
115113
S.Diag(Loc, isa<ObjCMethodDecl>(S.CurContext)
116114
? diag::err_coroutine_objc_method
117115
: diag::err_coroutine_outside_function) << Keyword;
118-
} else if (isa<CXXConstructorDecl>(FD) || isa<CXXDestructorDecl>(FD)) {
119-
// Coroutines TS [special]/6:
120-
// A special member function shall not be a coroutine.
121-
//
122-
// FIXME: We assume that this really means that a coroutine cannot
123-
// be a constructor or destructor.
124-
S.Diag(Loc, diag::err_coroutine_ctor_dtor)
125-
<< isa<CXXDestructorDecl>(FD) << Keyword;
126-
} else if (FD->isConstexpr()) {
127-
S.Diag(Loc, diag::err_coroutine_constexpr) << Keyword;
128-
} else if (FD->isVariadic()) {
129-
S.Diag(Loc, diag::err_coroutine_varargs) << Keyword;
130-
} else if (FD->isMain()) {
131-
S.Diag(FD->getLocStart(), diag::err_coroutine_main);
132-
S.Diag(Loc, diag::note_declared_coroutine_here)
133-
<< (Keyword == "co_await" ? 0 :
134-
Keyword == "co_yield" ? 1 : 2);
135-
} else {
136-
auto *ScopeInfo = S.getCurFunction();
137-
assert(ScopeInfo && "missing function scope for function");
138-
139-
// If we don't have a promise variable, build one now.
140-
if (!ScopeInfo->CoroutinePromise) {
141-
QualType T =
142-
FD->getType()->isDependentType()
143-
? S.Context.DependentTy
144-
: lookupPromiseType(S, FD->getType()->castAs<FunctionProtoType>(),
145-
Loc);
146-
if (T.isNull())
147-
return nullptr;
148-
149-
// Create and default-initialize the promise.
150-
ScopeInfo->CoroutinePromise =
151-
VarDecl::Create(S.Context, FD, FD->getLocation(), FD->getLocation(),
152-
&S.PP.getIdentifierTable().get("__promise"), T,
153-
S.Context.getTrivialTypeSourceInfo(T, Loc), SC_None);
154-
S.CheckVariableDeclarationType(ScopeInfo->CoroutinePromise);
155-
if (!ScopeInfo->CoroutinePromise->isInvalidDecl())
156-
S.ActOnUninitializedDecl(ScopeInfo->CoroutinePromise, false);
157-
}
116+
return false;
117+
}
118+
119+
// An enumeration for mapping the diagnostic type to the correct diagnostic
120+
// selection index.
121+
enum InvalidFuncDiag {
122+
DiagCtor = 0,
123+
DiagDtor,
124+
DiagCopyAssign,
125+
DiagMoveAssign,
126+
DiagMain,
127+
DiagConstexpr,
128+
DiagAutoRet,
129+
DiagVarargs,
130+
};
131+
bool Diagnosed = false;
132+
auto DiagInvalid = [&](InvalidFuncDiag ID) {
133+
S.Diag(Loc, diag::err_coroutine_invalid_func_context) << ID << Keyword;
134+
Diagnosed = true;
135+
return false;
136+
};
137+
138+
// Diagnose when a constructor, destructor, copy/move assignment operator,
139+
// or the function 'main' are declared as a coroutine.
140+
auto *MD = dyn_cast<CXXMethodDecl>(FD);
141+
if (MD && isa<CXXConstructorDecl>(MD))
142+
return DiagInvalid(DiagCtor);
143+
else if (MD && isa<CXXDestructorDecl>(MD))
144+
return DiagInvalid(DiagDtor);
145+
else if (MD && MD->isCopyAssignmentOperator())
146+
return DiagInvalid(DiagCopyAssign);
147+
else if (MD && MD->isMoveAssignmentOperator())
148+
return DiagInvalid(DiagMoveAssign);
149+
else if (FD->isMain())
150+
return DiagInvalid(DiagMain);
151+
152+
// Emit a diagnostics for each of the following conditions which is not met.
153+
if (FD->isConstexpr())
154+
DiagInvalid(DiagConstexpr);
155+
if (FD->getReturnType()->isUndeducedType())
156+
DiagInvalid(DiagAutoRet);
157+
if (FD->isVariadic())
158+
DiagInvalid(DiagVarargs);
159+
160+
return !Diagnosed;
161+
}
162+
163+
/// Check that this is a context in which a coroutine suspension can appear.
164+
static FunctionScopeInfo *checkCoroutineContext(Sema &S, SourceLocation Loc,
165+
StringRef Keyword) {
166+
if (!isValidCoroutineContext(S, Loc, Keyword))
167+
return nullptr;
158168

159-
return ScopeInfo;
169+
assert(isa<FunctionDecl>(S.CurContext) && "not in a function scope");
170+
auto *FD = cast<FunctionDecl>(S.CurContext);
171+
auto *ScopeInfo = S.getCurFunction();
172+
assert(ScopeInfo && "missing function scope for function");
173+
174+
// If we don't have a promise variable, build one now.
175+
if (!ScopeInfo->CoroutinePromise) {
176+
QualType T = FD->getType()->isDependentType()
177+
? S.Context.DependentTy
178+
: lookupPromiseType(
179+
S, FD->getType()->castAs<FunctionProtoType>(), Loc);
180+
if (T.isNull())
181+
return nullptr;
182+
183+
// Create and default-initialize the promise.
184+
ScopeInfo->CoroutinePromise =
185+
VarDecl::Create(S.Context, FD, FD->getLocation(), FD->getLocation(),
186+
&S.PP.getIdentifierTable().get("__promise"), T,
187+
S.Context.getTrivialTypeSourceInfo(T, Loc), SC_None);
188+
S.CheckVariableDeclarationType(ScopeInfo->CoroutinePromise);
189+
if (!ScopeInfo->CoroutinePromise->isInvalidDecl())
190+
S.ActOnUninitializedDecl(ScopeInfo->CoroutinePromise, false);
160191
}
161192

162-
return nullptr;
193+
return ScopeInfo;
163194
}
164195

165196
static Expr *buildBuiltinCall(Sema &S, SourceLocation Loc, Builtin::ID Id,

test/SemaCXX/coroutines.cpp

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,19 @@ struct CtorDtor {
178178
}
179179
// FIXME: The spec says this is ill-formed.
180180
void operator=(CtorDtor&) {
181-
co_yield 0;
181+
co_yield 0; // expected-error {{'co_yield' cannot be used in a copy assignment operator}}
182+
}
183+
void operator=(CtorDtor const &) {
184+
co_yield 0; // expected-error {{'co_yield' cannot be used in a copy assignment operator}}
185+
}
186+
void operator=(CtorDtor &&) {
187+
co_await a; // expected-error {{'co_await' cannot be used in a move assignment operator}}
188+
}
189+
void operator=(CtorDtor const &&) {
190+
co_await a; // expected-error {{'co_await' cannot be used in a move assignment operator}}
191+
}
192+
void operator=(int) {
193+
co_await a; // OK. Not a special member
182194
}
183195
};
184196

@@ -191,14 +203,19 @@ void unevaluated() {
191203
typeid(co_yield a); // expected-error {{cannot be used in an unevaluated context}}
192204
}
193205

194-
constexpr void constexpr_coroutine() {
206+
constexpr auto constexpr_deduced_return_coroutine() {
195207
co_yield 0; // expected-error {{'co_yield' cannot be used in a constexpr function}}
208+
// expected-error@-1 {{'co_yield' cannot be used in a function with a deduced return type}}
196209
}
197210

198211
void varargs_coroutine(const char *, ...) {
199212
co_await a; // expected-error {{'co_await' cannot be used in a varargs function}}
200213
}
201214

215+
auto deduced_return_coroutine() {
216+
co_await a; // expected-error {{'co_await' cannot be used in a function with a deduced return type}}
217+
}
218+
202219
struct outer {};
203220

204221
namespace dependent_operator_co_await_lookup {
@@ -355,6 +372,6 @@ coro<bad_promise_8> calls_set_exception() {
355372
template<> struct std::experimental::coroutine_traits<int, int, const char**>
356373
{ using promise_type = promise; };
357374

358-
int main(int, const char**) { // expected-error {{'main' cannot be a coroutine}}
359-
co_await a; // expected-note {{function is a coroutine due to use of 'co_await' here}}
375+
int main(int, const char**) {
376+
co_await a; // expected-error {{'co_await' cannot be used in the 'main' function}}
360377
}

0 commit comments

Comments
 (0)