[C++20] [Coroutines] Exit early if we found co_await appears in

unevaluated context

Closes https://github.com/llvm/llvm-project/issues/58133

The direct cause for this issue is that the compilation process
continues after it found it is in a invalid state. [expr.await]p2 says
clearly that the co_await expressions are not allowed to appear in
unevaluated context. So we can exit early in this case. It also reduces
many redundant diagnostic messages (Such as 'expression with side
effects has no effect in an unevaluated context').
This commit is contained in:
Chuanqi Xu 2022-10-09 14:54:32 +08:00
parent bc5e969ca1
commit b72a364bb5
5 changed files with 85 additions and 31 deletions

View File

@ -768,27 +768,34 @@ static bool isWithinCatchScope(Scope *S) {
// function-body *outside of a handler* [...] A context within a function
// where an await-expression can appear is called a suspension context of the
// function."
static void checkSuspensionContext(Sema &S, SourceLocation Loc,
static bool checkSuspensionContext(Sema &S, SourceLocation Loc,
StringRef Keyword) {
// First emphasis of [expr.await]p2: must be a potentially evaluated context.
// That is, 'co_await' and 'co_yield' cannot appear in subexpressions of
// \c sizeof.
if (S.isUnevaluatedContext())
if (S.isUnevaluatedContext()) {
S.Diag(Loc, diag::err_coroutine_unevaluated_context) << Keyword;
return false;
}
// Second emphasis of [expr.await]p2: must be outside of an exception handler.
if (isWithinCatchScope(S.getCurScope()))
if (isWithinCatchScope(S.getCurScope())) {
S.Diag(Loc, diag::err_coroutine_within_handler) << Keyword;
return false;
}
return true;
}
ExprResult Sema::ActOnCoawaitExpr(Scope *S, SourceLocation Loc, Expr *E) {
if (!checkSuspensionContext(*this, Loc, "co_await"))
return ExprError();
if (!ActOnCoroutineBodyStart(S, Loc, "co_await")) {
CorrectDelayedTyposInExpr(E);
return ExprError();
}
checkSuspensionContext(*this, Loc, "co_await");
if (E->hasPlaceholderType()) {
ExprResult R = CheckPlaceholderExpr(E);
if (R.isInvalid()) return ExprError();
@ -905,13 +912,14 @@ ExprResult Sema::BuildResolvedCoawaitExpr(SourceLocation Loc, Expr *Operand,
}
ExprResult Sema::ActOnCoyieldExpr(Scope *S, SourceLocation Loc, Expr *E) {
if (!checkSuspensionContext(*this, Loc, "co_yield"))
return ExprError();
if (!ActOnCoroutineBodyStart(S, Loc, "co_yield")) {
CorrectDelayedTyposInExpr(E);
return ExprError();
}
checkSuspensionContext(*this, Loc, "co_yield");
// Build yield_value call.
ExprResult Awaitable = buildPromiseCall(
*this, getCurFunction()->CoroutinePromise, Loc, "yield_value", E);

View File

@ -10,12 +10,14 @@ struct coroutine_traits { using promise_type = typename Ret::promise_type; };
template <class Promise = void>
struct coroutine_handle {
static coroutine_handle from_address(void *) noexcept;
constexpr void* address() const noexcept;
};
template <>
struct coroutine_handle<void> {
template <class PromiseType>
coroutine_handle(coroutine_handle<PromiseType>) noexcept;
static coroutine_handle from_address(void *);
constexpr void* address() const noexcept;
};
struct suspend_always {

View File

@ -0,0 +1,34 @@
// RUN: %clang_cc1 %s -std=c++20 -fsyntax-only -verify
#include "Inputs/std-coroutine.h"
struct MyTask{
struct promise_type {
MyTask get_return_object();
std::suspend_always initial_suspend() { return {}; }
void unhandled_exception();
void return_void();
auto final_suspend() noexcept {
struct Awaiter {
bool await_ready() noexcept { return false; }
std::coroutine_handle<promise_type> await_suspend(std::coroutine_handle<promise_type> h) noexcept;
void await_resume() noexcept;
};
return Awaiter{};
}
// The coroutine to resume when we're done.
std::coroutine_handle<promise_type> resume_when_done;
};
};
MyTask DoSomething() {
static_assert(__is_same(void, decltype(co_await 0))); // expected-error {{'co_await' cannot be used in an unevaluated context}}
co_return;
}
MyTask DoAnotherthing() {
static_assert(__is_same(void, decltype(co_yield 0))); // expected-error {{'co_yield' cannot be used in an unevaluated context}}
co_return;
}

View File

@ -334,21 +334,26 @@ class type_info;
void unevaluated() {
decltype(co_await a); // expected-error {{'co_await' cannot be used in an unevaluated context}}
// expected-warning@-1 {{declaration does not declare anything}}
sizeof(co_await a); // expected-error {{'co_await' cannot be used in an unevaluated context}}
// expected-error@-1 {{invalid application of 'sizeof' to an incomplete type 'void'}}
// expected-warning@-2 {{expression with side effects has no effect in an unevaluated context}}
typeid(co_await a); // expected-error {{'co_await' cannot be used in an unevaluated context}}
// expected-warning@-1 {{expression with side effects has no effect in an unevaluated context}}
// expected-warning@-2 {{expression result unused}}
}
void unevaluated2() {
sizeof(co_await a); // expected-error {{'co_await' cannot be used in an unevaluated context}}
}
void unevaluated3() {
typeid(co_await a); // expected-error {{'co_await' cannot be used in an unevaluated context}}
}
void unevaluated4() {
decltype(co_yield 1); // expected-error {{'co_yield' cannot be used in an unevaluated context}}
// expected-warning@-1 {{declaration does not declare anything}}
sizeof(co_yield 2); // expected-error {{'co_yield' cannot be used in an unevaluated context}}
// expected-error@-1 {{invalid application of 'sizeof' to an incomplete type 'void'}}
// expected-warning@-2 {{expression with side effects has no effect in an unevaluated context}}
typeid(co_yield 3); // expected-error {{'co_yield' cannot be used in an unevaluated context}}
// expected-warning@-1 {{expression with side effects has no effect in an unevaluated context}}
// expected-warning@-2 {{expression result unused}}
}
void unevaluated5() {
sizeof(co_yield 2); // expected-error {{'co_yield' cannot be used in an unevaluated context}}
}
void unevaluated6() {
typeid(co_yield 3); // expected-error {{'co_yield' cannot be used in an unevaluated context}}
}
// [expr.await]p2: "An await-expression shall not appear in a default argument."

View File

@ -323,21 +323,26 @@ namespace std { class type_info; }
void unevaluated() {
decltype(co_await a); // expected-error {{'co_await' cannot be used in an unevaluated context}}
// expected-warning@-1 {{declaration does not declare anything}}
}
void unevaluated2() {
sizeof(co_await a); // expected-error {{'co_await' cannot be used in an unevaluated context}}
// expected-error@-1 {{invalid application of 'sizeof' to an incomplete type 'void'}}
// expected-warning@-2 {{expression with side effects has no effect in an unevaluated context}}
}
void unevaluated3() {
typeid(co_await a); // expected-error {{'co_await' cannot be used in an unevaluated context}}
// expected-warning@-1 {{expression with side effects has no effect in an unevaluated context}}
// expected-warning@-2 {{expression result unused}}
}
void unevaluated4() {
decltype(co_yield 1); // expected-error {{'co_yield' cannot be used in an unevaluated context}}
// expected-warning@-1 {{declaration does not declare anything}}
}
void unevaluated5() {
sizeof(co_yield 2); // expected-error {{'co_yield' cannot be used in an unevaluated context}}
// expected-error@-1 {{invalid application of 'sizeof' to an incomplete type 'void'}}
// expected-warning@-2 {{expression with side effects has no effect in an unevaluated context}}
}
void unevaluated6() {
typeid(co_yield 3); // expected-error {{'co_yield' cannot be used in an unevaluated context}}
// expected-warning@-1 {{expression with side effects has no effect in an unevaluated context}}
// expected-warning@-2 {{expression result unused}}
}
// [expr.await]p2: "An await-expression shall not appear in a default argument."