diff --git a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp --- a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp +++ b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp @@ -509,6 +509,50 @@ {"template concept Fooable = true"}); } +TEST_F(TargetDeclTest, Coroutine) { + Flags.push_back("-std=c++20"); + + Code = R"cpp( + namespace std::experimental { + template struct coroutine_traits; + template struct coroutine_handle { + template + coroutine_handle(coroutine_handle&&) 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) 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 { + 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. diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h --- a/clang/include/clang/AST/ExprCXX.h +++ b/clang/include/clang/AST/ExprCXX.h @@ -4681,18 +4681,19 @@ 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; @@ -4701,10 +4702,11 @@ } 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; @@ -4713,6 +4715,7 @@ } CoroutineSuspendExpr(StmtClass SC, EmptyShell Empty) : Expr(SC, Empty) { + SubExprs[SubExpr::Operand] = nullptr; SubExprs[SubExpr::Common] = nullptr; SubExprs[SubExpr::Ready] = nullptr; SubExprs[SubExpr::Suspend] = nullptr; @@ -4725,6 +4728,10 @@ return static_cast(SubExprs[SubExpr::Common]); } + Expr *getOperand() const { + return static_cast(SubExprs[SubExpr::Operand]); + } + /// getOpaqueValue - Return the opaque value placeholder. OpaqueValueExpr *getOpaqueValue() const { return OpaqueValue; } @@ -4743,7 +4750,7 @@ SourceLocation getBeginLoc() const LLVM_READONLY { return KeywordLoc; } SourceLocation getEndLoc() const LLVM_READONLY { - return getCommonExpr()->getEndLoc(); + return getOperand()->getEndLoc(); } child_range children() { @@ -4765,28 +4772,24 @@ 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; } @@ -4850,20 +4853,18 @@ 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; } diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -10301,8 +10301,8 @@ 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 BuildResolvedCoawaitExpr(SourceLocation KwLoc, Expr *Operand, + Expr *E, bool IsImplicit = false); ExprResult BuildUnresolvedCoawaitExpr(SourceLocation KwLoc, Expr *E, UnresolvedLookupExpr* Lookup); ExprResult BuildCoyieldExpr(SourceLocation KwLoc, Expr *E); diff --git a/clang/lib/Sema/SemaCoroutine.cpp b/clang/lib/Sema/SemaCoroutine.cpp --- a/clang/lib/Sema/SemaCoroutine.cpp +++ b/clang/lib/Sema/SemaCoroutine.cpp @@ -734,7 +734,7 @@ Suspend = buildOperatorCoawaitCall(*this, SC, Loc, Suspend.get()); if (Suspend.isInvalid()) return StmtError(); - Suspend = BuildResolvedCoawaitExpr(Loc, Suspend.get(), + Suspend = BuildResolvedCoawaitExpr(Loc, Suspend.get(), Suspend.get(), /*IsImplicit*/ true); Suspend = ActOnFinishFullExpr(Suspend.get(), /*DiscardedValue*/ false); if (Suspend.isInvalid()) { @@ -835,6 +835,8 @@ E = R.get(); } + Expr *Operand = E; + auto *Promise = FSI->CoroutinePromise; if (Promise->getType()->isDependentType()) { Expr *Res = @@ -857,11 +859,11 @@ if (Awaitable.isInvalid()) return ExprError(); - return BuildResolvedCoawaitExpr(Loc, Awaitable.get()); + return BuildResolvedCoawaitExpr(Loc, Operand, Awaitable.get()); } -ExprResult Sema::BuildResolvedCoawaitExpr(SourceLocation Loc, Expr *E, - bool IsImplicit) { +ExprResult Sema::BuildResolvedCoawaitExpr(SourceLocation Loc, Expr *Operand, + Expr *E, bool IsImplicit) { auto *Coroutine = checkCoroutineContext(*this, Loc, "co_await", IsImplicit); if (!Coroutine) return ExprError(); @@ -874,7 +876,7 @@ if (E->getType()->isDependentType()) { Expr *Res = new (Context) - CoawaitExpr(Loc, Context.DependentTy, E, IsImplicit); + CoawaitExpr(Loc, Context.DependentTy, Operand, E, IsImplicit); return Res; } @@ -895,7 +897,7 @@ return ExprError(); Expr *Res = - new (Context) CoawaitExpr(Loc, E, RSS.Results[0], RSS.Results[1], + new (Context) CoawaitExpr(Loc, Operand, E, RSS.Results[0], RSS.Results[1], RSS.Results[2], RSS.OpaqueValue, IsImplicit); return Res; @@ -933,8 +935,10 @@ 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; } @@ -950,7 +954,7 @@ 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; diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -1470,9 +1470,10 @@ /// /// 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, - bool IsImplicit) { - return getSema().BuildResolvedCoawaitExpr(CoawaitLoc, Result, IsImplicit); + ExprResult RebuildCoawaitExpr(SourceLocation CoawaitLoc, Expr *Operand, + Expr *Result, bool IsImplicit) { + return getSema().BuildResolvedCoawaitExpr(CoawaitLoc, Operand, Result, + IsImplicit); } /// Build a new co_await expression. @@ -7930,15 +7931,23 @@ template ExprResult TreeTransform::TransformCoawaitExpr(CoawaitExpr *E) { - ExprResult Result = getDerived().TransformInitializer(E->getOperand(), - /*NotCopyInit*/false); - if (Result.isInvalid()) + // XXX is transforming the operand and the common-expr separately the + // right thing to do? + + ExprResult Operand = getDerived().TransformInitializer(E->getOperand(), + /*NotCopyInit*/ false); + if (Operand.isInvalid()) + return ExprError(); + + ExprResult Common = getDerived().TransformInitializer(E->getCommonExpr(), + /*NotCopyInit*/ false); + if (Common.isInvalid()) return ExprError(); // 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(), + Common.get(), E->isImplicit()); } template diff --git a/clang/test/SemaCXX/co_await-ast.cpp b/clang/test/SemaCXX/co_await-ast.cpp new file mode 100644 --- /dev/null +++ b/clang/test/SemaCXX/co_await-ast.cpp @@ -0,0 +1,97 @@ +// RUN: %clang_cc1 -std=c++20 -fsyntax-only -ast-dump -ast-dump-filter=foo %s | FileCheck %s --strict-whitespace + +namespace std { +template struct coroutine_traits; +template struct coroutine_handle { + template + coroutine_handle(coroutine_handle &&) 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) 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 { + 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 {{.*}} '' .await_transform {{.*}} +// CHECK: | | | `-DeclRefExpr {{.*}} 'std::coroutine_traits::promise_type':'awaitable_frame' lvalue Var {{.*}} '__promise' 'std::coroutine_traits::promise_type':'awaitable_frame' +// CHECK: | | `-CXXTemporaryObjectExpr {{.*}} 'executor' 'void () noexcept' zeroing +// CHECK: | |-ExprWithCleanups {{.*}} 'bool' +// CHECK: | | `-CXXMemberCallExpr {{.*}} 'bool' +// CHECK: | | `-MemberExpr {{.*}} '' .await_ready {{.*}} +// CHECK: | | `-ImplicitCastExpr {{.*}} 'const awaitable_frame::result_t' lvalue +// 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 {{.*}} '' .await_transform {{.*}} +// CHECK: | | | `-DeclRefExpr {{.*}} 'std::coroutine_traits::promise_type':'awaitable_frame' lvalue Var {{.*}} '__promise' 'std::coroutine_traits::promise_type':'awaitable_frame' +// CHECK: | | `-CXXTemporaryObjectExpr {{.*}} 'executor' 'void () noexcept' zeroing +// CHECK: | |-ExprWithCleanups {{.*}} 'void' +// CHECK: | | `-CXXMemberCallExpr {{.*}} 'void' +// CHECK: | | |-MemberExpr {{.*}} '' .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 {{.*}} '' .await_transform {{.*}} +// CHECK: | | | | `-DeclRefExpr {{.*}} 'std::coroutine_traits::promise_type':'awaitable_frame' lvalue Var {{.*}} '__promise' 'std::coroutine_traits::promise_type':'awaitable_frame' +// CHECK: | | | `-CXXTemporaryObjectExpr {{.*}} 'executor' 'void () noexcept' zeroing +// CHECK: | | `-ImplicitCastExpr {{.*}} 'std::coroutine_handle':'std::coroutine_handle' +// CHECK: | | `-CXXConstructExpr {{.*}} 'std::coroutine_handle':'std::coroutine_handle' 'void (coroutine_handle &&) noexcept' +// CHECK: | | `-MaterializeTemporaryExpr {{.*}} 'std::coroutine_handle' xvalue +// CHECK: | | `-CallExpr {{.*}} 'std::coroutine_handle' +// CHECK: | | |-ImplicitCastExpr {{.*}} 'std::coroutine_handle (*)(void *) noexcept' +// CHECK: | | | `-DeclRefExpr {{.*}} 'std::coroutine_handle (void *) noexcept' lvalue CXXMethod {{.*}} 'from_address' 'std::coroutine_handle (void *) noexcept' +// CHECK: | | `-CallExpr {{.*}} 'void *' +// CHECK: | | `-ImplicitCastExpr {{.*}} 'void *(*)() noexcept' +// CHECK: | | `-DeclRefExpr {{.*}} 'void *() noexcept' lvalue Function {{.*}} '__builtin_coro_frame' 'void *() noexcept' +// CHECK: | `-CXXMemberCallExpr {{.*}} 'void' +// CHECK: | `-MemberExpr {{.*}} '' .await_resume {{.*}} +// CHECK: | `-ImplicitCastExpr {{.*}} 'const awaitable_frame::result_t' lvalue +// 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 {{.*}} '' .await_transform {{.*}} +// CHECK: | | `-DeclRefExpr {{.*}} 'std::coroutine_traits::promise_type':'awaitable_frame' lvalue Var {{.*}} '__promise' 'std::coroutine_traits::promise_type':'awaitable_frame' +// CHECK: | `-CXXTemporaryObjectExpr {{.*}} 'executor' 'void () noexcept' zeroing + +// Rest of the generated coroutine statements omitted.