clang 18.0.0git
Classes | Functions
SemaCoroutine.cpp File Reference
#include "CoroutineStmtBuilder.h"
#include "clang/AST/ASTLambda.h"
#include "clang/AST/Decl.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/StmtCXX.h"
#include "clang/Basic/Builtins.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Sema/Initialization.h"
#include "clang/Sema/Overload.h"
#include "clang/Sema/ScopeInfo.h"
#include "clang/Sema/SemaInternal.h"
#include "llvm/ADT/SmallSet.h"

Go to the source code of this file.

Classes

struct  ReadySuspendResumeResult
 

Functions

static LookupResult lookupMember (Sema &S, const char *Name, CXXRecordDecl *RD, SourceLocation Loc, bool &Res)
 
static bool lookupMember (Sema &S, const char *Name, CXXRecordDecl *RD, SourceLocation Loc)
 
static QualType lookupPromiseType (Sema &S, const FunctionDecl *FD, SourceLocation KwLoc)
 Look up the std::coroutine_traits<...>::promise_type for the given function type.
 
static QualType lookupCoroutineHandleType (Sema &S, QualType PromiseType, SourceLocation Loc)
 Look up the std::coroutine_handle<PromiseType>.
 
static bool isValidCoroutineContext (Sema &S, SourceLocation Loc, StringRef Keyword)
 
static ExprResult buildOperatorCoawaitCall (Sema &SemaRef, Scope *S, SourceLocation Loc, Expr *E)
 
static ExprResult buildCoroutineHandle (Sema &S, QualType PromiseType, SourceLocation Loc)
 
static ExprResult buildMemberCall (Sema &S, Expr *Base, SourceLocation Loc, StringRef Name, MultiExprArg Args)
 
static ExprmaybeTailCall (Sema &S, QualType RetType, Expr *E, SourceLocation Loc)
 
static void tryMarkAwaitSuspendNoInline (Sema &S, OpaqueValueExpr *Awaiter, CallExpr *AwaitSuspend)
 The await_suspend call performed by co_await is essentially asynchronous to the execution of the coroutine.
 
static ReadySuspendResumeResult buildCoawaitCalls (Sema &S, VarDecl *CoroPromise, SourceLocation Loc, Expr *E)
 Build calls to await_ready, await_suspend, and await_resume for a co_await expression.
 
static ExprResult buildPromiseCall (Sema &S, VarDecl *Promise, SourceLocation Loc, StringRef Name, MultiExprArg Args)
 
static FunctionScopeInfocheckCoroutineContext (Sema &S, SourceLocation Loc, StringRef Keyword, bool IsImplicit=false)
 Check that this is a context in which a coroutine suspension can appear.
 
static void checkNoThrow (Sema &S, const Stmt *E, llvm::SmallPtrSetImpl< const Decl * > &ThrowingDecls)
 Recursively check E and all its children to see if any call target (including constructor call) is declared noexcept.
 
static bool isWithinCatchScope (Scope *S)
 
static bool checkSuspensionContext (Sema &S, SourceLocation Loc, StringRef Keyword)
 
static ExprbuildStdNoThrowDeclRef (Sema &S, SourceLocation Loc)
 Look up the std::nothrow object.
 
static TypeSourceInfogetTypeSourceInfoForStdAlignValT (Sema &S, SourceLocation Loc)
 
static bool findDeleteForPromise (Sema &S, SourceLocation Loc, QualType PromiseType, FunctionDecl *&OperatorDelete)
 
static CompoundStmtbuildCoroutineBody (Stmt *Body, ASTContext &Context)
 
static bool diagReturnOnAllocFailure (Sema &S, Expr *E, CXXRecordDecl *PromiseRecordDecl, FunctionScopeInfo &Fn)
 
static bool collectPlacementArgs (Sema &S, FunctionDecl &FD, SourceLocation Loc, SmallVectorImpl< Expr * > &PlacementArgs)
 
static void noteMemberDeclaredHere (Sema &S, Expr *E, FunctionScopeInfo &Fn)
 
static ExprcastForMoving (Sema &S, Expr *E, QualType T=QualType())
 
static VarDeclbuildVarDecl (Sema &S, SourceLocation Loc, QualType Type, IdentifierInfo *II)
 Build a variable declaration for move parameter.
 

Function Documentation

◆ buildCoawaitCalls()

static ReadySuspendResumeResult buildCoawaitCalls ( Sema S,
VarDecl CoroPromise,
SourceLocation  Loc,
Expr E 
)
static

Build calls to await_ready, await_suspend, and await_resume for a co_await expression.

The generated AST tries to clean up temporary objects as early as possible so that they don't live across suspension points if possible. Having temporary objects living across suspension points unnecessarily can lead to large frame size, and also lead to memory corruptions if the coroutine frame is destroyed after coming back from suspension. This is done by wrapping both the await_ready call and the await_suspend call with ExprWithCleanups. In the end of this function, we also need to explicitly set cleanup state so that the CoawaitExpr is also wrapped with an ExprWithCleanups to clean up the awaiter associated with the co_await expression.

Definition at line 454 of file SemaCoroutine.cpp.

References buildCoroutineHandle(), buildMemberCall(), clang::Sema::Cleanup, clang::Sema::Context, clang::Sema::Diag(), clang::ActionResult< PtrTy, Compress >::get(), clang::DeclaratorDecl::getBeginLoc(), clang::CallExpr::getCalleeDecl(), clang::CallExpr::getCallReturnType(), clang::CallExpr::getDirectCallee(), clang::Decl::getLocation(), clang::Expr::getObjectKind(), clang::Stmt::getSourceRange(), clang::ValueDecl::getType(), clang::Expr::getType(), clang::Type::isBooleanType(), clang::Type::isDependentType(), clang::ActionResult< PtrTy, Compress >::isInvalid(), ReadySuspendResumeResult::IsInvalid, clang::Type::isReferenceType(), clang::Type::isVoidType(), clang::Sema::MaybeCreateExprWithCleanups(), maybeTailCall(), clang::Sema::PerformContextuallyConvertToBool(), clang::Result, ReadySuspendResumeResult::Results, clang::CleanupInfo::setExprNeedsCleanups(), tryMarkAwaitSuspendNoInline(), and clang::VK_LValue.

Referenced by clang::Sema::BuildCoyieldExpr(), and clang::Sema::BuildResolvedCoawaitExpr().

◆ buildCoroutineBody()

static CompoundStmt * buildCoroutineBody ( Stmt Body,
ASTContext Context 
)
static

◆ buildCoroutineHandle()

static ExprResult buildCoroutineHandle ( Sema S,
QualType  PromiseType,
SourceLocation  Loc 
)
static

◆ buildMemberCall()

static ExprResult buildMemberCall ( Sema S,
Expr Base,
SourceLocation  Loc,
StringRef  Name,
MultiExprArg  Args 
)
static

◆ buildOperatorCoawaitCall()

static ExprResult buildOperatorCoawaitCall ( Sema SemaRef,
Scope S,
SourceLocation  Loc,
Expr E 
)
static

◆ buildPromiseCall()

static ExprResult buildPromiseCall ( Sema S,
VarDecl Promise,
SourceLocation  Loc,
StringRef  Name,
MultiExprArg  Args 
)
static

◆ buildStdNoThrowDeclRef()

static Expr * buildStdNoThrowDeclRef ( Sema S,
SourceLocation  Loc 
)
static

◆ buildVarDecl()

static VarDecl * buildVarDecl ( Sema S,
SourceLocation  Loc,
QualType  Type,
IdentifierInfo II 
)
static

◆ castForMoving()

static Expr * castForMoving ( Sema S,
Expr E,
QualType  T = QualType() 
)
static

◆ checkCoroutineContext()

static FunctionScopeInfo * checkCoroutineContext ( Sema S,
SourceLocation  Loc,
StringRef  Keyword,
bool  IsImplicit = false 
)
static

◆ checkNoThrow()

static void checkNoThrow ( Sema S,
const Stmt E,
llvm::SmallPtrSetImpl< const Decl * > &  ThrowingDecls 
)
static

Recursively check E and all its children to see if any call target (including constructor call) is declared noexcept.

Also any value returned from the call has a noexcept destructor.

Definition at line 697 of file SemaCoroutine.cpp.

References clang::Sema::canCalleeThrow(), checkNoThrow(), clang::Stmt::children(), clang::Sema::CurContext, clang::Sema::Diag(), clang::QualType::DK_cxx_destructor, clang::Sema::getASTContext(), clang::QualType::getCanonicalType(), clang::CXXRecordDecl::getDestructor(), clang::CXXMethodDecl::getParent(), clang::QualType::getTypePtr(), and clang::QualType::isDestructedType().

Referenced by clang::Sema::checkFinalSuspendNoThrow(), and checkNoThrow().

◆ checkSuspensionContext()

static bool checkSuspensionContext ( Sema S,
SourceLocation  Loc,
StringRef  Keyword 
)
static

◆ collectPlacementArgs()

static bool collectPlacementArgs ( Sema S,
FunctionDecl FD,
SourceLocation  Loc,
SmallVectorImpl< Expr * > &  PlacementArgs 
)
static

◆ diagReturnOnAllocFailure()

static bool diagReturnOnAllocFailure ( Sema S,
Expr E,
CXXRecordDecl PromiseRecordDecl,
FunctionScopeInfo Fn 
)
static

◆ findDeleteForPromise()

static bool findDeleteForPromise ( Sema S,
SourceLocation  Loc,
QualType  PromiseType,
FunctionDecl *&  OperatorDelete 
)
static

◆ getTypeSourceInfoForStdAlignValT()

static TypeSourceInfo * getTypeSourceInfoForStdAlignValT ( Sema S,
SourceLocation  Loc 
)
static

◆ isValidCoroutineContext()

static bool isValidCoroutineContext ( Sema S,
SourceLocation  Loc,
StringRef  Keyword 
)
static

Definition at line 181 of file SemaCoroutine.cpp.

References clang::Sema::CurContext, and clang::Sema::Diag().

Referenced by checkCoroutineContext().

◆ isWithinCatchScope()

static bool isWithinCatchScope ( Scope S)
static

Definition at line 825 of file SemaCoroutine.cpp.

Referenced by checkSuspensionContext().

◆ lookupCoroutineHandleType()

static QualType lookupCoroutineHandleType ( Sema S,
QualType  PromiseType,
SourceLocation  Loc 
)
static

◆ lookupMember() [1/2]

static bool lookupMember ( Sema S,
const char *  Name,
CXXRecordDecl RD,
SourceLocation  Loc 
)
static

Definition at line 43 of file SemaCoroutine.cpp.

References lookupMember().

◆ lookupMember() [2/2]

static LookupResult lookupMember ( Sema S,
const char *  Name,
CXXRecordDecl RD,
SourceLocation  Loc,
bool Res 
)
static

◆ lookupPromiseType()

static QualType lookupPromiseType ( Sema S,
const FunctionDecl FD,
SourceLocation  KwLoc 
)
static

◆ maybeTailCall()

static Expr * maybeTailCall ( Sema S,
QualType  RetType,
Expr E,
SourceLocation  Loc 
)
static

◆ noteMemberDeclaredHere()

static void noteMemberDeclaredHere ( Sema S,
Expr E,
FunctionScopeInfo Fn 
)
static

◆ tryMarkAwaitSuspendNoInline()

static void tryMarkAwaitSuspendNoInline ( Sema S,
OpaqueValueExpr Awaiter,
CallExpr AwaitSuspend 
)
static

The await_suspend call performed by co_await is essentially asynchronous to the execution of the coroutine.

Inlining it normally into an unsplit coroutine can cause miscompilation because the coroutine CFG misrepresents the true control flow of the program: things that happen in the await_suspend are not guaranteed to happen prior to the resumption of the coroutine, and things that happen after the resumption of the coroutine (including its exit and the potential deallocation of the coroutine frame) are not guaranteed to happen only after the end of await_suspend.

See https://github.com/llvm/llvm-project/issues/56301 and https://reviews.llvm.org/D157070 for the example and the full discussion.

The short-term solution to this problem is to mark the call as uninlinable. But we don't want to do this if the call is known to be trivial, which is very common.

The long-term solution may introduce patterns like:

call @llvm.coro.await_suspend(ptr awaiter, ptr handle, ptr @awaitSuspendFn)

Then it is much easier to perform the safety analysis in the middle end. If it is safe to inline the call to awaitSuspend, we can replace it in the CoroEarly pass. Otherwise we could replace it in the CoroSplit pass.

Definition at line 409 of file SemaCoroutine.cpp.

References clang::Decl::addAttr(), clang::RecordDecl::field_empty(), clang::Type::getAsCXXRecordDecl(), clang::Sema::getASTContext(), clang::CallExpr::getDirectCallee(), clang::Decl::getLocation(), clang::QualType::getNonReferenceType(), clang::Expr::getType(), and clang::Decl::hasAttr().

Referenced by buildCoawaitCalls().