{"template <typename T, typename U> concept Fooable = true"});
}
+TEST_F(TargetDeclTest, Coroutine) {
+ Flags.push_back("-std=c++20");
+
+ Code = R"cpp(
+ namespace std::experimental {
+ template <typename, typename...> struct coroutine_traits;
+ template <typename> struct coroutine_handle {
+ template <typename U>
+ coroutine_handle(coroutine_handle<U>&&) noexcept;
+ static coroutine_handle from_address(void* __addr) noexcept;
+ };
+ } // namespace std::experimental
+
+ struct executor {};
+ struct awaitable {};
+ struct awaitable_frame {
+ awaitable get_return_object();
+ void return_void();
+ void unhandled_exception();
+ struct result_t {
+ ~result_t();
+ bool await_ready() const noexcept;
+ void await_suspend(std::experimental::coroutine_handle<void>) noexcept;
+ void await_resume() const noexcept;
+ };
+ result_t initial_suspend() noexcept;
+ result_t final_suspend() noexcept;
+ result_t await_transform(executor) noexcept;
+ };
+
+ namespace std::experimental {
+ template <>
+ struct coroutine_traits<awaitable> {
+ typedef awaitable_frame promise_type;
+ };
+ } // namespace std::experimental
+
+ awaitable foo() {
+ co_await [[executor]]();
+ }
+ )cpp";
+ EXPECT_DECLS("RecordTypeLoc", "struct executor");
+}
+
TEST_F(TargetDeclTest, FunctionTemplate) {
Code = R"cpp(
// Implicit specialization.
SourceLocation KeywordLoc;
- enum SubExpr { Common, Ready, Suspend, Resume, Count };
+ enum SubExpr { Operand, Common, Ready, Suspend, Resume, Count };
Stmt *SubExprs[SubExpr::Count];
OpaqueValueExpr *OpaqueValue = nullptr;
public:
- CoroutineSuspendExpr(StmtClass SC, SourceLocation KeywordLoc, Expr *Common,
- Expr *Ready, Expr *Suspend, Expr *Resume,
+ CoroutineSuspendExpr(StmtClass SC, SourceLocation KeywordLoc, Expr *Operand,
+ Expr *Common, Expr *Ready, Expr *Suspend, Expr *Resume,
OpaqueValueExpr *OpaqueValue)
: Expr(SC, Resume->getType(), Resume->getValueKind(),
Resume->getObjectKind()),
KeywordLoc(KeywordLoc), OpaqueValue(OpaqueValue) {
+ SubExprs[SubExpr::Operand] = Operand;
SubExprs[SubExpr::Common] = Common;
SubExprs[SubExpr::Ready] = Ready;
SubExprs[SubExpr::Suspend] = Suspend;
}
CoroutineSuspendExpr(StmtClass SC, SourceLocation KeywordLoc, QualType Ty,
- Expr *Common)
+ Expr *Operand, Expr *Common)
: Expr(SC, Ty, VK_PRValue, OK_Ordinary), KeywordLoc(KeywordLoc) {
assert(Common->isTypeDependent() && Ty->isDependentType() &&
"wrong constructor for non-dependent co_await/co_yield expression");
+ SubExprs[SubExpr::Operand] = Operand;
SubExprs[SubExpr::Common] = Common;
SubExprs[SubExpr::Ready] = nullptr;
SubExprs[SubExpr::Suspend] = nullptr;
}
CoroutineSuspendExpr(StmtClass SC, EmptyShell Empty) : Expr(SC, Empty) {
+ SubExprs[SubExpr::Operand] = nullptr;
SubExprs[SubExpr::Common] = nullptr;
SubExprs[SubExpr::Ready] = nullptr;
SubExprs[SubExpr::Suspend] = nullptr;
SubExprs[SubExpr::Resume] = nullptr;
}
- SourceLocation getKeywordLoc() const { return KeywordLoc; }
-
Expr *getCommonExpr() const {
return static_cast<Expr*>(SubExprs[SubExpr::Common]);
}
return static_cast<Expr*>(SubExprs[SubExpr::Resume]);
}
+ // The syntactic operand written in the code
+ Expr *getOperand() const {
+ return static_cast<Expr *>(SubExprs[SubExpr::Operand]);
+ }
+
+ SourceLocation getKeywordLoc() const { return KeywordLoc; }
+
SourceLocation getBeginLoc() const LLVM_READONLY { return KeywordLoc; }
SourceLocation getEndLoc() const LLVM_READONLY {
- return getCommonExpr()->getEndLoc();
+ return getOperand()->getEndLoc();
}
child_range children() {
friend class ASTStmtReader;
public:
- CoawaitExpr(SourceLocation CoawaitLoc, Expr *Operand, Expr *Ready,
- Expr *Suspend, Expr *Resume, OpaqueValueExpr *OpaqueValue,
- bool IsImplicit = false)
- : CoroutineSuspendExpr(CoawaitExprClass, CoawaitLoc, Operand, Ready,
- Suspend, Resume, OpaqueValue) {
+ CoawaitExpr(SourceLocation CoawaitLoc, Expr *Operand, Expr *Common,
+ Expr *Ready, Expr *Suspend, Expr *Resume,
+ OpaqueValueExpr *OpaqueValue, bool IsImplicit = false)
+ : CoroutineSuspendExpr(CoawaitExprClass, CoawaitLoc, Operand, Common,
+ Ready, Suspend, Resume, OpaqueValue) {
CoawaitBits.IsImplicit = IsImplicit;
}
CoawaitExpr(SourceLocation CoawaitLoc, QualType Ty, Expr *Operand,
- bool IsImplicit = false)
- : CoroutineSuspendExpr(CoawaitExprClass, CoawaitLoc, Ty, Operand) {
+ Expr *Common, bool IsImplicit = false)
+ : CoroutineSuspendExpr(CoawaitExprClass, CoawaitLoc, Ty, Operand,
+ Common) {
CoawaitBits.IsImplicit = IsImplicit;
}
CoawaitExpr(EmptyShell Empty)
: CoroutineSuspendExpr(CoawaitExprClass, Empty) {}
- Expr *getOperand() const {
- // FIXME: Dig out the actual operand or store it.
- return getCommonExpr();
- }
-
bool isImplicit() const { return CoawaitBits.IsImplicit; }
void setIsImplicit(bool value = true) { CoawaitBits.IsImplicit = value; }
friend class ASTStmtReader;
public:
- CoyieldExpr(SourceLocation CoyieldLoc, Expr *Operand, Expr *Ready,
- Expr *Suspend, Expr *Resume, OpaqueValueExpr *OpaqueValue)
- : CoroutineSuspendExpr(CoyieldExprClass, CoyieldLoc, Operand, Ready,
- Suspend, Resume, OpaqueValue) {}
- CoyieldExpr(SourceLocation CoyieldLoc, QualType Ty, Expr *Operand)
- : CoroutineSuspendExpr(CoyieldExprClass, CoyieldLoc, Ty, Operand) {}
+ CoyieldExpr(SourceLocation CoyieldLoc, Expr *Operand, Expr *Common,
+ Expr *Ready, Expr *Suspend, Expr *Resume,
+ OpaqueValueExpr *OpaqueValue)
+ : CoroutineSuspendExpr(CoyieldExprClass, CoyieldLoc, Operand, Common,
+ Ready, Suspend, Resume, OpaqueValue) {}
+ CoyieldExpr(SourceLocation CoyieldLoc, QualType Ty, Expr *Operand,
+ Expr *Common)
+ : CoroutineSuspendExpr(CoyieldExprClass, CoyieldLoc, Ty, Operand,
+ Common) {}
CoyieldExpr(EmptyShell Empty)
: CoroutineSuspendExpr(CoyieldExprClass, Empty) {}
- Expr *getOperand() const {
- // FIXME: Dig out the actual operand or store it.
- return getCommonExpr();
- }
-
static bool classof(const Stmt *T) {
return T->getStmtClass() == CoyieldExprClass;
}
ExprResult ActOnCoyieldExpr(Scope *S, SourceLocation KwLoc, Expr *E);
StmtResult ActOnCoreturnStmt(Scope *S, SourceLocation KwLoc, Expr *E);
- ExprResult BuildResolvedCoawaitExpr(SourceLocation KwLoc, Expr *E,
- bool IsImplicit = false);
- ExprResult BuildUnresolvedCoawaitExpr(SourceLocation KwLoc, Expr *E,
- UnresolvedLookupExpr* Lookup);
+ ExprResult BuildOperatorCoawaitLookupExpr(Scope *S, SourceLocation Loc);
+ ExprResult BuildOperatorCoawaitCall(SourceLocation Loc, Expr *E,
+ UnresolvedLookupExpr *Lookup);
+ ExprResult BuildResolvedCoawaitExpr(SourceLocation KwLoc, Expr *Operand,
+ Expr *Awaiter, bool IsImplicit = false);
+ ExprResult BuildUnresolvedCoawaitExpr(SourceLocation KwLoc, Expr *Operand,
+ UnresolvedLookupExpr *Lookup);
ExprResult BuildCoyieldExpr(SourceLocation KwLoc, Expr *E);
StmtResult BuildCoreturnStmt(SourceLocation KwLoc, Expr *E,
bool IsImplicit = false);
if (!ChildExpr)
continue;
+ if (auto *CSE = dyn_cast<CoroutineSuspendExpr>(E))
+ if (ChildExpr == CSE->getOperand())
+ // Do not recurse over a CoroutineSuspendExpr's operand.
+ // The operand is also a subexpression of getCommonExpr(), and
+ // recursing into it directly would produce duplicate diagnostics.
+ continue;
+
if (IsLogicalAndOperator &&
isa<StringLiteral>(ChildExpr->IgnoreParenImpCasts()))
// Ignore checking string literals that are in logical and operators.
return !Diagnosed;
}
-static ExprResult buildOperatorCoawaitLookupExpr(Sema &SemaRef, Scope *S,
- SourceLocation Loc) {
- DeclarationName OpName =
- SemaRef.Context.DeclarationNames.getCXXOperatorName(OO_Coawait);
- LookupResult Operators(SemaRef, OpName, SourceLocation(),
- Sema::LookupOperatorName);
- SemaRef.LookupName(Operators, S);
-
- assert(!Operators.isAmbiguous() && "Operator lookup cannot be ambiguous");
- const auto &Functions = Operators.asUnresolvedSet();
- bool IsOverloaded =
- Functions.size() > 1 ||
- (Functions.size() == 1 && isa<FunctionTemplateDecl>(*Functions.begin()));
- Expr *CoawaitOp = UnresolvedLookupExpr::Create(
- SemaRef.Context, /*NamingClass*/ nullptr, NestedNameSpecifierLoc(),
- DeclarationNameInfo(OpName, Loc), /*RequiresADL*/ true, IsOverloaded,
- Functions.begin(), Functions.end());
- assert(CoawaitOp);
- return CoawaitOp;
-}
-
/// Build a call to 'operator co_await' if there is a suitable operator for
/// the given expression.
-static ExprResult buildOperatorCoawaitCall(Sema &SemaRef, SourceLocation Loc,
- Expr *E,
- UnresolvedLookupExpr *Lookup) {
+ExprResult Sema::BuildOperatorCoawaitCall(SourceLocation Loc, Expr *E,
+ UnresolvedLookupExpr *Lookup) {
UnresolvedSet<16> Functions;
Functions.append(Lookup->decls_begin(), Lookup->decls_end());
- return SemaRef.CreateOverloadedUnaryOp(Loc, UO_Coawait, Functions, E);
+ return CreateOverloadedUnaryOp(Loc, UO_Coawait, Functions, E);
}
static ExprResult buildOperatorCoawaitCall(Sema &SemaRef, Scope *S,
SourceLocation Loc, Expr *E) {
- ExprResult R = buildOperatorCoawaitLookupExpr(SemaRef, S, Loc);
+ ExprResult R = SemaRef.BuildOperatorCoawaitLookupExpr(S, Loc);
if (R.isInvalid())
return ExprError();
- return buildOperatorCoawaitCall(SemaRef, Loc, E,
- cast<UnresolvedLookupExpr>(R.get()));
+ return SemaRef.BuildOperatorCoawaitCall(Loc, E,
+ cast<UnresolvedLookupExpr>(R.get()));
}
static ExprResult buildCoroutineHandle(Sema &S, QualType PromiseType,
SourceLocation Loc = Fn->getLocation();
// Build the initial suspend point
auto buildSuspends = [&](StringRef Name) mutable -> StmtResult {
- ExprResult Suspend =
+ ExprResult Operand =
buildPromiseCall(*this, ScopeInfo->CoroutinePromise, Loc, Name, None);
- if (Suspend.isInvalid())
+ if (Operand.isInvalid())
return StmtError();
- Suspend = buildOperatorCoawaitCall(*this, SC, Loc, Suspend.get());
+ ExprResult Suspend =
+ buildOperatorCoawaitCall(*this, SC, Loc, Operand.get());
if (Suspend.isInvalid())
return StmtError();
- Suspend = BuildResolvedCoawaitExpr(Loc, Suspend.get(),
+ Suspend = BuildResolvedCoawaitExpr(Loc, Operand.get(), Suspend.get(),
/*IsImplicit*/ true);
Suspend = ActOnFinishFullExpr(Suspend.get(), /*DiscardedValue*/ false);
if (Suspend.isInvalid()) {
if (R.isInvalid()) return ExprError();
E = R.get();
}
- ExprResult Lookup = buildOperatorCoawaitLookupExpr(*this, S, Loc);
+ ExprResult Lookup = BuildOperatorCoawaitLookupExpr(S, Loc);
if (Lookup.isInvalid())
return ExprError();
return BuildUnresolvedCoawaitExpr(Loc, E,
cast<UnresolvedLookupExpr>(Lookup.get()));
}
-ExprResult Sema::BuildUnresolvedCoawaitExpr(SourceLocation Loc, Expr *E,
+ExprResult Sema::BuildOperatorCoawaitLookupExpr(Scope *S, SourceLocation Loc) {
+ DeclarationName OpName =
+ Context.DeclarationNames.getCXXOperatorName(OO_Coawait);
+ LookupResult Operators(*this, OpName, SourceLocation(),
+ Sema::LookupOperatorName);
+ LookupName(Operators, S);
+
+ assert(!Operators.isAmbiguous() && "Operator lookup cannot be ambiguous");
+ const auto &Functions = Operators.asUnresolvedSet();
+ bool IsOverloaded =
+ Functions.size() > 1 ||
+ (Functions.size() == 1 && isa<FunctionTemplateDecl>(*Functions.begin()));
+ Expr *CoawaitOp = UnresolvedLookupExpr::Create(
+ Context, /*NamingClass*/ nullptr, NestedNameSpecifierLoc(),
+ DeclarationNameInfo(OpName, Loc), /*RequiresADL*/ true, IsOverloaded,
+ Functions.begin(), Functions.end());
+ assert(CoawaitOp);
+ return CoawaitOp;
+}
+
+// Attempts to resolve and build a CoawaitExpr from "raw" inputs, bailing out to
+// DependentCoawaitExpr if needed.
+ExprResult Sema::BuildUnresolvedCoawaitExpr(SourceLocation Loc, Expr *Operand,
UnresolvedLookupExpr *Lookup) {
auto *FSI = checkCoroutineContext(*this, Loc, "co_await");
if (!FSI)
return ExprError();
- if (E->hasPlaceholderType()) {
- ExprResult R = CheckPlaceholderExpr(E);
+ if (Operand->hasPlaceholderType()) {
+ ExprResult R = CheckPlaceholderExpr(Operand);
if (R.isInvalid())
return ExprError();
- E = R.get();
+ Operand = R.get();
}
auto *Promise = FSI->CoroutinePromise;
if (Promise->getType()->isDependentType()) {
- Expr *Res =
- new (Context) DependentCoawaitExpr(Loc, Context.DependentTy, E, Lookup);
+ Expr *Res = new (Context)
+ DependentCoawaitExpr(Loc, Context.DependentTy, Operand, Lookup);
return Res;
}
auto *RD = Promise->getType()->getAsCXXRecordDecl();
+ auto *Transformed = Operand;
if (lookupMember(*this, "await_transform", RD, Loc)) {
- ExprResult R = buildPromiseCall(*this, Promise, Loc, "await_transform", E);
+ ExprResult R =
+ buildPromiseCall(*this, Promise, Loc, "await_transform", Operand);
if (R.isInvalid()) {
Diag(Loc,
diag::note_coroutine_promise_implicit_await_transform_required_here)
- << E->getSourceRange();
+ << Operand->getSourceRange();
return ExprError();
}
- E = R.get();
+ Transformed = R.get();
}
- ExprResult Awaitable = buildOperatorCoawaitCall(*this, Loc, E, Lookup);
- if (Awaitable.isInvalid())
+ ExprResult Awaiter = BuildOperatorCoawaitCall(Loc, Transformed, Lookup);
+ if (Awaiter.isInvalid())
return ExprError();
- return BuildResolvedCoawaitExpr(Loc, Awaitable.get());
+ return BuildResolvedCoawaitExpr(Loc, Operand, Awaiter.get());
}
-ExprResult Sema::BuildResolvedCoawaitExpr(SourceLocation Loc, Expr *E,
- bool IsImplicit) {
+ExprResult Sema::BuildResolvedCoawaitExpr(SourceLocation Loc, Expr *Operand,
+ Expr *Awaiter, bool IsImplicit) {
auto *Coroutine = checkCoroutineContext(*this, Loc, "co_await", IsImplicit);
if (!Coroutine)
return ExprError();
- if (E->hasPlaceholderType()) {
- ExprResult R = CheckPlaceholderExpr(E);
+ if (Awaiter->hasPlaceholderType()) {
+ ExprResult R = CheckPlaceholderExpr(Awaiter);
if (R.isInvalid()) return ExprError();
- E = R.get();
+ Awaiter = R.get();
}
- if (E->getType()->isDependentType()) {
+ if (Awaiter->getType()->isDependentType()) {
Expr *Res = new (Context)
- CoawaitExpr(Loc, Context.DependentTy, E, IsImplicit);
+ CoawaitExpr(Loc, Context.DependentTy, Operand, Awaiter, IsImplicit);
return Res;
}
// If the expression is a temporary, materialize it as an lvalue so that we
// can use it multiple times.
- if (E->isPRValue())
- E = CreateMaterializeTemporaryExpr(E->getType(), E, true);
+ if (Awaiter->isPRValue())
+ Awaiter = CreateMaterializeTemporaryExpr(Awaiter->getType(), Awaiter, true);
// The location of the `co_await` token cannot be used when constructing
// the member call expressions since it's before the location of `Expr`, which
// is used as the start of the member call expression.
- SourceLocation CallLoc = E->getExprLoc();
+ SourceLocation CallLoc = Awaiter->getExprLoc();
// Build the await_ready, await_suspend, await_resume calls.
- ReadySuspendResumeResult RSS = buildCoawaitCalls(
- *this, Coroutine->CoroutinePromise, CallLoc, E);
+ ReadySuspendResumeResult RSS =
+ buildCoawaitCalls(*this, Coroutine->CoroutinePromise, CallLoc, Awaiter);
if (RSS.IsInvalid)
return ExprError();
- Expr *Res =
- new (Context) CoawaitExpr(Loc, E, RSS.Results[0], RSS.Results[1],
- RSS.Results[2], RSS.OpaqueValue, IsImplicit);
+ Expr *Res = new (Context)
+ CoawaitExpr(Loc, Operand, Awaiter, RSS.Results[0], RSS.Results[1],
+ RSS.Results[2], RSS.OpaqueValue, IsImplicit);
return Res;
}
E = R.get();
}
+ Expr *Operand = E;
+
if (E->getType()->isDependentType()) {
- Expr *Res = new (Context) CoyieldExpr(Loc, Context.DependentTy, E);
+ Expr *Res = new (Context) CoyieldExpr(Loc, Context.DependentTy, Operand, E);
return Res;
}
return ExprError();
Expr *Res =
- new (Context) CoyieldExpr(Loc, E, RSS.Results[0], RSS.Results[1],
+ new (Context) CoyieldExpr(Loc, Operand, E, RSS.Results[0], RSS.Results[1],
RSS.Results[2], RSS.OpaqueValue);
return Res;
///
/// By default, performs semantic analysis to build the new expression.
/// Subclasses may override this routine to provide different behavior.
- ExprResult RebuildCoawaitExpr(SourceLocation CoawaitLoc, Expr *Result,
+ ExprResult RebuildCoawaitExpr(SourceLocation CoawaitLoc, Expr *Operand,
+ UnresolvedLookupExpr *OpCoawaitLookup,
bool IsImplicit) {
- return getSema().BuildResolvedCoawaitExpr(CoawaitLoc, Result, IsImplicit);
+ // This function rebuilds a coawait-expr given its operator.
+ // For an explicit coawait-expr, the rebuild involves the full set
+ // of transformations performed by BuildUnresolvedCoawaitExpr(),
+ // including calling await_transform().
+ // For an implicit coawait-expr, we need to rebuild the "operator
+ // coawait" but not await_transform(), so use BuildResolvedCoawaitExpr().
+ // This mirrors how the implicit CoawaitExpr is originally created
+ // in Sema::ActOnCoroutineBodyStart().
+ if (IsImplicit) {
+ ExprResult Suspend = getSema().BuildOperatorCoawaitCall(
+ CoawaitLoc, Operand, OpCoawaitLookup);
+ if (Suspend.isInvalid())
+ return ExprError();
+ return getSema().BuildResolvedCoawaitExpr(CoawaitLoc, Operand,
+ Suspend.get(), true);
+ }
+
+ return getSema().BuildUnresolvedCoawaitExpr(CoawaitLoc, Operand,
+ OpCoawaitLookup);
}
/// Build a new co_await expression.
S->isImplicit());
}
-template<typename Derived>
-ExprResult
-TreeTransform<Derived>::TransformCoawaitExpr(CoawaitExpr *E) {
- ExprResult Result = getDerived().TransformInitializer(E->getOperand(),
- /*NotCopyInit*/false);
- if (Result.isInvalid())
+template <typename Derived>
+ExprResult TreeTransform<Derived>::TransformCoawaitExpr(CoawaitExpr *E) {
+ ExprResult Operand = getDerived().TransformInitializer(E->getOperand(),
+ /*NotCopyInit*/ false);
+ if (Operand.isInvalid())
return ExprError();
+ // Rebuild the common-expr from the operand rather than transforming it
+ // separately.
+
+ // 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.
+ ExprResult Lookup = getSema().BuildOperatorCoawaitLookupExpr(
+ getSema().getCurScope(), E->getKeywordLoc());
+
// Always rebuild; we don't know if this needs to be injected into a new
// context or if the promise type has changed.
- return getDerived().RebuildCoawaitExpr(E->getKeywordLoc(), Result.get(),
- E->isImplicit());
+ return getDerived().RebuildCoawaitExpr(
+ E->getKeywordLoc(), Operand.get(),
+ cast<UnresolvedLookupExpr>(Lookup.get()), E->isImplicit());
}
template <typename Derived>
// CHECK: CaseStmt
// CHECK: ExprWithCleanups {{.*}} 'void'
// CHECK-NEXT: CoawaitExpr
-// CHECK-NEXT: MaterializeTemporaryExpr {{.*}} 'Task::Awaiter':'Task::Awaiter'
+// CHECK-NEXT: CXXBindTemporaryExpr {{.*}} 'Task' (CXXTemporary {{.*}})
+// CHECK: MaterializeTemporaryExpr {{.*}} 'Task::Awaiter':'Task::Awaiter'
// CHECK: ExprWithCleanups {{.*}} 'bool'
// CHECK-NEXT: CXXMemberCallExpr {{.*}} 'bool'
// CHECK-NEXT: MemberExpr {{.*}} .await_ready
// CHECK: CaseStmt
// CHECK: ExprWithCleanups {{.*}} 'void'
// CHECK-NEXT: CoawaitExpr
-// CHECK-NEXT: MaterializeTemporaryExpr {{.*}} 'Task::Awaiter':'Task::Awaiter'
+// CHECK-NEXT: CXXBindTemporaryExpr {{.*}} 'Task' (CXXTemporary {{.*}})
+// CHECK: MaterializeTemporaryExpr {{.*}} 'Task::Awaiter':'Task::Awaiter'
// CHECK: ExprWithCleanups {{.*}} 'bool'
// CHECK-NEXT: CXXMemberCallExpr {{.*}} 'bool'
// CHECK-NEXT: MemberExpr {{.*}} .await_ready
// CHECK: CaseStmt
// CHECK: ExprWithCleanups {{.*}} 'void'
// CHECK-NEXT: CoawaitExpr
-// CHECK-NEXT: MaterializeTemporaryExpr {{.*}} 'Task::Awaiter':'Task::Awaiter'
+// CHECK-NEXT: CXXBindTemporaryExpr {{.*}} 'Task' (CXXTemporary {{.*}})
+// CHECK: MaterializeTemporaryExpr {{.*}} 'Task::Awaiter':'Task::Awaiter'
// CHECK: ExprWithCleanups {{.*}} 'bool'
// CHECK-NEXT: CXXMemberCallExpr {{.*}} 'bool'
// CHECK-NEXT: MemberExpr {{.*}} .await_ready
// CHECK: CaseStmt
// CHECK: ExprWithCleanups {{.*}} 'void'
// CHECK-NEXT: CoawaitExpr
-// CHECK-NEXT: MaterializeTemporaryExpr {{.*}} 'Task::Awaiter':'Task::Awaiter'
+// CHECK-NEXT: CXXBindTemporaryExpr {{.*}} 'Task' (CXXTemporary {{.*}})
+// CHECK: MaterializeTemporaryExpr {{.*}} 'Task::Awaiter':'Task::Awaiter'
// CHECK: ExprWithCleanups {{.*}} 'bool'
// CHECK-NEXT: CXXMemberCallExpr {{.*}} 'bool'
// CHECK-NEXT: MemberExpr {{.*}} .await_ready
A a{};
// CHECK: CoawaitExpr {{0x[0-9a-fA-F]+}} <col:3, col:12>
// CHECK-NEXT: DeclRefExpr {{0x[0-9a-fA-F]+}} <col:12>
+ // CHECK-NEXT: DeclRefExpr {{0x[0-9a-fA-F]+}} <col:12>
// CHECK-NEXT: CXXMemberCallExpr {{0x[0-9a-fA-F]+}} <col:12>
// CHECK-NEXT: MemberExpr {{0x[0-9a-fA-F]+}} <col:12>
co_await a;
A a{};
// CHECK: CoawaitExpr {{0x[0-9a-fA-F]+}} <col:3, col:12>
// CHECK-NEXT: DeclRefExpr {{0x[0-9a-fA-F]+}} <col:12>
+ // CHECK-NEXT: DeclRefExpr {{0x[0-9a-fA-F]+}} <col:12>
// CHECK-NEXT: CXXMemberCallExpr {{0x[0-9a-fA-F]+}} <col:12>
// CHECK-NEXT: MemberExpr {{0x[0-9a-fA-F]+}} <col:12>
co_await a;
--- /dev/null
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -ast-dump -ast-dump-filter=foo %s | FileCheck %s --strict-whitespace
+
+namespace std {
+template <typename, typename...> struct coroutine_traits;
+template <typename> struct coroutine_handle {
+ template <typename U>
+ coroutine_handle(coroutine_handle<U> &&) noexcept;
+ static coroutine_handle from_address(void *__addr) noexcept;
+};
+} // namespace std
+
+struct executor {};
+struct awaitable {};
+struct awaitable_frame {
+ awaitable get_return_object();
+ void return_void();
+ void unhandled_exception();
+ struct result_t {
+ ~result_t();
+ bool await_ready() const noexcept;
+ void await_suspend(std::coroutine_handle<void>) noexcept;
+ void await_resume() const noexcept;
+ };
+ result_t initial_suspend() noexcept;
+ result_t final_suspend() noexcept;
+ result_t await_transform(executor) noexcept;
+};
+
+namespace std {
+template <>
+struct coroutine_traits<awaitable> {
+ typedef awaitable_frame promise_type;
+};
+} // namespace std
+
+awaitable foo() {
+ co_await executor();
+}
+
+// Check that CoawaitExpr contains the correct subexpressions, including
+// the operand expression as written in the source.
+
+// CHECK-LABEL: Dumping foo:
+// CHECK: FunctionDecl {{.*}} foo 'awaitable ()'
+// CHECK: `-CoroutineBodyStmt {{.*}}
+// CHECK: |-CompoundStmt {{.*}}
+// CHECK: | `-ExprWithCleanups {{.*}} 'void'
+// CHECK: | `-CoawaitExpr {{.*}} 'void'
+// CHECK: | |-CXXTemporaryObjectExpr {{.*}} 'executor' 'void () noexcept' zeroing
+// CHECK: | |-MaterializeTemporaryExpr {{.*}} 'awaitable_frame::result_t' lvalue
+// CHECK: | | `-CXXBindTemporaryExpr {{.*}} 'awaitable_frame::result_t' (CXXTemporary {{.*}})
+// CHECK: | | `-CXXMemberCallExpr {{.*}} 'awaitable_frame::result_t'
+// CHECK: | | |-MemberExpr {{.*}} '<bound member function type>' .await_transform {{.*}}
+// CHECK: | | | `-DeclRefExpr {{.*}} 'std::coroutine_traits<awaitable>::promise_type':'awaitable_frame' lvalue Var {{.*}} '__promise' 'std::coroutine_traits<awaitable>::promise_type':'awaitable_frame'
+// CHECK: | | `-CXXTemporaryObjectExpr {{.*}} 'executor' 'void () noexcept' zeroing
+// CHECK: | |-ExprWithCleanups {{.*}} 'bool'
+// CHECK: | | `-CXXMemberCallExpr {{.*}} 'bool'
+// CHECK: | | `-MemberExpr {{.*}} '<bound member function type>' .await_ready {{.*}}
+// CHECK: | | `-ImplicitCastExpr {{.*}} 'const awaitable_frame::result_t' lvalue <NoOp>
+// CHECK: | | `-OpaqueValueExpr {{.*}} 'awaitable_frame::result_t' lvalue
+// CHECK: | | `-MaterializeTemporaryExpr {{.*}} 'awaitable_frame::result_t' lvalue
+// CHECK: | | `-CXXBindTemporaryExpr {{.*}} 'awaitable_frame::result_t' (CXXTemporary {{.*}})
+// CHECK: | | `-CXXMemberCallExpr {{.*}} 'awaitable_frame::result_t'
+// CHECK: | | |-MemberExpr {{.*}} '<bound member function type>' .await_transform {{.*}}
+// CHECK: | | | `-DeclRefExpr {{.*}} 'std::coroutine_traits<awaitable>::promise_type':'awaitable_frame' lvalue Var {{.*}} '__promise' 'std::coroutine_traits<awaitable>::promise_type':'awaitable_frame'
+// CHECK: | | `-CXXTemporaryObjectExpr {{.*}} 'executor' 'void () noexcept' zeroing
+// CHECK: | |-ExprWithCleanups {{.*}} 'void'
+// CHECK: | | `-CXXMemberCallExpr {{.*}} 'void'
+// CHECK: | | |-MemberExpr {{.*}} '<bound member function type>' .await_suspend {{.*}}
+// CHECK: | | | `-OpaqueValueExpr {{.*}} 'awaitable_frame::result_t' lvalue
+// CHECK: | | | `-MaterializeTemporaryExpr {{.*}} 'awaitable_frame::result_t' lvalue
+// CHECK: | | | `-CXXBindTemporaryExpr {{.*}} 'awaitable_frame::result_t' (CXXTemporary {{.*}})
+// CHECK: | | | `-CXXMemberCallExpr {{.*}} 'awaitable_frame::result_t'
+// CHECK: | | | |-MemberExpr {{.*}} '<bound member function type>' .await_transform {{.*}}
+// CHECK: | | | | `-DeclRefExpr {{.*}} 'std::coroutine_traits<awaitable>::promise_type':'awaitable_frame' lvalue Var {{.*}} '__promise' 'std::coroutine_traits<awaitable>::promise_type':'awaitable_frame'
+// CHECK: | | | `-CXXTemporaryObjectExpr {{.*}} 'executor' 'void () noexcept' zeroing
+// CHECK: | | `-ImplicitCastExpr {{.*}} 'std::coroutine_handle<void>':'std::coroutine_handle<void>' <ConstructorConversion>
+// CHECK: | | `-CXXConstructExpr {{.*}} 'std::coroutine_handle<void>':'std::coroutine_handle<void>' 'void (coroutine_handle<awaitable_frame> &&) noexcept'
+// CHECK: | | `-MaterializeTemporaryExpr {{.*}} 'std::coroutine_handle<awaitable_frame>' xvalue
+// CHECK: | | `-CallExpr {{.*}} 'std::coroutine_handle<awaitable_frame>'
+// CHECK: | | |-ImplicitCastExpr {{.*}} 'std::coroutine_handle<awaitable_frame> (*)(void *) noexcept' <FunctionToPointerDecay>
+// CHECK: | | | `-DeclRefExpr {{.*}} 'std::coroutine_handle<awaitable_frame> (void *) noexcept' lvalue CXXMethod {{.*}} 'from_address' 'std::coroutine_handle<awaitable_frame> (void *) noexcept'
+// CHECK: | | `-CallExpr {{.*}} 'void *'
+// CHECK: | | `-ImplicitCastExpr {{.*}} 'void *(*)() noexcept' <FunctionToPointerDecay>
+// CHECK: | | `-DeclRefExpr {{.*}} 'void *() noexcept' lvalue Function {{.*}} '__builtin_coro_frame' 'void *() noexcept'
+// CHECK: | `-CXXMemberCallExpr {{.*}} 'void'
+// CHECK: | `-MemberExpr {{.*}} '<bound member function type>' .await_resume {{.*}}
+// CHECK: | `-ImplicitCastExpr {{.*}} 'const awaitable_frame::result_t' lvalue <NoOp>
+// CHECK: | `-OpaqueValueExpr {{.*}} 'awaitable_frame::result_t' lvalue
+// CHECK: | `-MaterializeTemporaryExpr {{.*}} 'awaitable_frame::result_t' lvalue
+// CHECK: | `-CXXBindTemporaryExpr {{.*}} 'awaitable_frame::result_t' (CXXTemporary {{.*}})
+// CHECK: | `-CXXMemberCallExpr {{.*}} 'awaitable_frame::result_t'
+// CHECK: | |-MemberExpr {{.*}} '<bound member function type>' .await_transform {{.*}}
+// CHECK: | | `-DeclRefExpr {{.*}} 'std::coroutine_traits<awaitable>::promise_type':'awaitable_frame' lvalue Var {{.*}} '__promise' 'std::coroutine_traits<awaitable>::promise_type':'awaitable_frame'
+// CHECK: | `-CXXTemporaryObjectExpr {{.*}} <col:12, col:21> 'executor' 'void () noexcept' zeroing
+
+// Rest of the generated coroutine statements omitted.