21 namespace ranges = std::ranges;
35 using namespace asthelpers;
38 QualType CoroutinesCodeGenerator::GetFramePointerType()
const
40 return Ptr(GetFrameType());
46 RETURN_IF(not(mASTData.mFrameType and mASTData.mDoInsertInDtor));
48 mASTData.mFrameType->completeDefinition();
54 codeGenerator->
InsertArg(mASTData.mFrameType);
57 mOutputFormatHelper.InsertAt(mPosBeforeFunc, ofm);
75 FieldDecl* CoroutinesCodeGenerator::AddField(std::string_view name, QualType type)
115 size_t& mSuspendsCount;
116 llvm::DenseMap<VarDecl*, MemberExpr*> mVarNamePrefix{};
120 size_t& suspendsCounter,
122 llvm::DenseMap<VarDecl*, MemberExpr*> varNamePrefix,
123 Stmt* prev =
nullptr)
125 , mASTData{coroutineASTData}
126 , mSuspendsCount{suspendsCounter}
127 , mVarNamePrefix{varNamePrefix}
129 if(
nullptr == mPrevStmt) {
145 for(
auto* child : stmt->body()) {
150 mBodyStmts.Add(child);
165 Visit(stmt->getCond());
172 Visit(stmt->getCond());
179 Visit(stmt->getCond());
186 Visit(stmt->getCond());
196 Visit(stmt->getInit());
201 auto* oldInit = stmt->getInit();
202 auto* newInit = mBodyStmts.mStmts.back();
203 mBodyStmts.mStmts.pop_back();
210 Visit(stmt->getCond());
212 Visit(stmt->getInc());
221 Visit(stmt->getRangeStmt());
230 if(
auto* vd = dyn_cast_or_null<VarDecl>(stmt->getDecl())) {
231 RETURN_IF(not vd->isLocalVarDeclOrParm() or vd->isStaticLocal() or not
Contains(mVarNamePrefix, vd));
233 auto* memberExpr = mVarNamePrefix[vd];
241 for(
auto* decl : stmt->decls()) {
242 if(
auto* varDecl = dyn_cast_or_null<VarDecl>(decl)) {
243 if(varDecl->isStaticLocal()) {
249 auto* field =
AddField(mASTData,
GetName(*varDecl), varDecl->getType());
251 auto* assign =
Assign(me, field, varDecl->getInit());
253 mVarNamePrefix.insert(std::make_pair(varDecl, me));
255 Visit(varDecl->getInit());
258 mBodyStmts.Add(assign);
260 }
else if(
const auto* recordDecl = dyn_cast_or_null<CXXRecordDecl>(decl)) {
261 mASTData.
mFrameType->addDecl(
const_cast<CXXRecordDecl*
>(recordDecl));
271 ReplaceNode(mPrevStmt, stmt, indirectThisMemberExpr);
280 auto* tmp = mPrevStmt;
283 for(
auto* arg : stmt->arguments()) {
292 auto* tmp = mPrevStmt;
293 mPrevStmt = stmt->getCallee();
295 Visit(stmt->getCallee());
304 Visit(stmt->getOperand());
305 Visit(stmt->getPromiseCall());
312 if(isa<ExprWithCleanups>(mStaged)) {
313 mBodyStmts.Add(stmt);
317 Visit(stmt->getOperand());
324 if(
const bool returnsVoid{stmt->getResumeExpr()->getType()->isVoidType()}; returnsVoid) {
325 Visit(stmt->getOperand());
331 mBodyStmts.Add(stmt);
336 auto* resultVar =
Variable(name, stmt->getType());
341 Visit(stmt->getCommonExpr());
342 Visit(stmt->getOperand());
343 Visit(stmt->getSuspendExpr());
344 Visit(stmt->getReadyExpr());
345 Visit(stmt->getResumeExpr());
350 auto* varDecl = stmt->getPromiseDecl();
355 mVarNamePrefix.insert(std::make_pair(varDecl, me));
368 for(
auto* param : stmt->getParamMoves()) {
369 if(
auto* declStmt = dyn_cast_or_null<DeclStmt>(param)) {
370 if(
auto* varDecl2 = dyn_cast_or_null<VarDecl>(declStmt->getSingleDecl())) {
372 if(
auto* declRef =
FindDeclRef(varDecl2->getAnyInitializer())) {
373 auto* varDecl = dyn_cast<ParmVarDecl>(declRef->getDecl());
375 auto* field =
AddField(mASTData,
GetName(*varDecl), varDecl->getType());
378 mVarNamePrefix.insert(std::make_pair(
const_cast<ParmVarDecl*
>(varDecl), me));
384 Visit(stmt->getBody());
386 Visit(stmt->getReturnStmt());
387 Visit(stmt->getReturnValue());
388 Visit(stmt->getReturnValueInit());
389 Visit(stmt->getExceptionHandler());
390 Visit(stmt->getReturnStmtOnAllocFailure());
391 Visit(stmt->getFallthroughHandler());
392 Visit(stmt->getInitSuspendStmt());
393 Visit(stmt->getFinalSuspendStmt());
398 auto* tmp = mPrevStmt;
401 for(
auto* child : stmt->children()) {
412 mOutputFormatHelper.OpenScope();
422 if(
const auto* args = fd.getTemplateSpecializationArgs()) {
425 for(
OnceFalse needsUnderscore{};
const auto& arg : args->asArray()) {
426 if(needsUnderscore) {
434 auto str = std::move(ofm.GetString());
441 if(fd.isOverloadedOperator()) {
451 mASTData.mFrameType =
Struct(mFrameName);
464 auto resumeFnType =
Ptr(resumeFnFd->getType());
468 auto destroyFnType =
Ptr(destroyFnFd->getType());
472 mOutputFormatHelper.AppendCommentNewLine(
"Allocate the frame including the promise"sv);
473 mOutputFormatHelper.AppendCommentNewLine(
"Note: The actual parameter new is __builtin_coro_size"sv);
476 auto* reicast =
ReinterpretCast(GetFramePointerType(), stmt->getAllocate());
478 coroFrameVar->setInit(reicast);
480 InsertArg(coroFrameVar);
484 if(stmt->getReturnStmtOnAllocFailure()) {
485 auto* nptr =
new(ctx) CXXNullPtrLiteralExpr({});
489 auto* ifStmt =
If(
Equal(nptr, mASTData.mFrameAccessDeclRef), bodyStmts);
491 mOutputFormatHelper.AppendNewLine();
496 mASTData, mSuspendsCounter,
const_cast<CoroutineBodyStmt*
>(stmt), llvm::DenseMap<VarDecl*, MemberExpr*>{}};
499 auto* setSuspendIndexToZero =
Assign(mASTData.mFrameAccessDeclRef, mASTData.mSuspendIndexField,
Int32(0));
500 InsertArgWithNull(setSuspendIndexToZero);
503 auto* initializeInitialAwaitResume =
504 Assign(mASTData.mFrameAccessDeclRef, mASTData.mInitialAwaitResumeCalledField,
Bool(
false));
505 InsertArgWithNull(initializeInitialAwaitResume);
508 for(
auto* param : stmt->getParamMoves()) {
509 if(
const auto* declStmt = dyn_cast_or_null<DeclStmt>(param)) {
510 if(
const auto* varDecl = dyn_cast_or_null<VarDecl>(declStmt->getSingleDecl())) {
511 const auto varName =
GetName(*varDecl);
528 ArrayRef<ParmVarDecl*> funParams = fd.parameters();
529 SmallVector<ParmVarDecl*, 16> funParamStorage{};
530 QualType cxxMethodType{};
532 if(
const auto* cxxMethodDecl = dyn_cast_or_null<CXXMethodDecl>(&fd)) {
533 funParamStorage.reserve(funParams.size() + 1);
535 cxxMethodType = cxxMethodDecl->getFunctionObjectParameterType();
541 ranges::copy(funParams, std::back_inserter(funParamStorage));
543 funParams = funParamStorage;
546 auto getNonRefType = [&](
auto* var) -> QualType {
547 if(
const auto* et = var->getType().getNonReferenceType()->template getAs<ElaboratedType>()) {
548 return et->getNamedType();
550 return QualType(var->getType().getNonReferenceType().getTypePtrOrNull(), 0);
554 SmallVector<Expr*, 16> exprs{};
556 for(
auto* promiseTypeRecordDecl = mASTData.mPromiseField->getType()->getAsCXXRecordDecl();
557 auto* ctor : promiseTypeRecordDecl->ctors()) {
559 if(not ranges::equal(
560 ctor->parameters(), funParams, [&](
auto& a,
auto& b) { return getNonRefType(a) == getNonRefType(b); })) {
568 if(not ctor->param_empty() and
569 (getNonRefType(ctor->getParamDecl(0)) == QualType(cxxMethodType.getTypePtrOrNull(), 0))) {
570 if(0 == mASTData.mThisExprs.size()) {
571 mASTData.mThisExprs.push_back(CXXThisExpr::Create(ctx, {},
Ptr(cxxMethodType),
false));
574 (void)
static_cast<bool>(derefFirstParam);
577 for(
const auto& fparam : funParams) {
578 if(derefFirstParam) {
582 exprs.push_back(
AccessMember(mASTData.mFrameAccessDeclRef, fparam));
586 if(funParams.size()) {
594 if(mASTData.mThisExprs.size()) {
599 mOutputFormatHelper.AppendNewLine();
600 mOutputFormatHelper.AppendCommentNewLine(
"Construct the promise."sv);
601 auto* me =
AccessMember(mASTData.mFrameAccessDeclRef, mASTData.mPromiseField);
603 auto* ctorArgs =
new(ctx) InitListExpr{ctx, {}, exprs, {}};
605 CXXNewExpr* newFrame =
New({
AddrOf(me)}, ctorArgs, mASTData.mPromiseField->getType());
607 InsertArgWithNull(newFrame);
614 mOutputFormatHelper.AppendNewLine();
617 mOutputFormatHelper.AppendCommentNewLine(
"Forward declare the resume and destroy function."sv);
620 InsertArg(fsmFuncDecl);
622 InsertArg(deallocFuncDecl);
624 mOutputFormatHelper.AppendNewLine();
626 mOutputFormatHelper.AppendCommentNewLine(
"Assign the resume and destroy function pointers."sv);
628 auto* assignResumeFn =
Assign(mASTData.mFrameAccessDeclRef, mASTData.mResumeFnField,
Ref(fsmFuncDecl));
629 InsertArgWithNull(assignResumeFn);
631 auto* assignDestroyFn =
Assign(mASTData.mFrameAccessDeclRef, mASTData.mDestroyFnField,
Ref(deallocFuncDecl));
632 InsertArgWithNull(assignDestroyFn);
633 mOutputFormatHelper.AppendNewLine();
635 mOutputFormatHelper.AppendCommentNewLine(
636 R
"A(Call the made up function with the coroutine body for initial suspend.
637 This function will be called subsequently by coroutine_handle<>::resume()
638 which calls __builtin_coro_resume(__handle_))A"sv);
640 auto* callCoroFSM =
Call(fsmFuncDecl, {mASTData.mFrameAccessDeclRef});
641 InsertArgWithNull(callCoroFSM);
643 mOutputFormatHelper.AppendNewLine();
644 mOutputFormatHelper.AppendNewLine();
646 InsertArg(stmt->getReturnStmt());
648 mOutputFormatHelper.AppendSemiNewLine();
650 mOutputFormatHelper.CloseScope(OutputFormatHelper::NoNewLineBefore::Yes);
651 mOutputFormatHelper.AppendNewLine();
652 mOutputFormatHelper.AppendNewLine();
657 mOutputFormatHelper.AppendCommentNewLine(
"This function invoked by coroutine_handle<>::resume()"sv);
659 InsertArg(fsmFuncDecl);
661 mASTData.mDoInsertInDtor =
true;
667 if(
auto* dtor = mASTData.mPromiseField->getType()->getAsCXXRecordDecl()->getDestructor()) {
668 deallocFuncBodyStmts.Add(
Comment(
"Deallocating the coroutine promise type"sv));
670 auto* promiseAccess =
AccessMember(mASTData.mFrameAccessDeclRef, mASTData.mPromiseField);
671 auto* deallocPromise =
AccessMember(promiseAccess, dtor,
false);
672 auto* dtorCall =
CallMemberFun(deallocPromise, dtor->getType());
673 deallocFuncBodyStmts.Add(dtorCall);
676 deallocFuncBodyStmts.Add(
677 Comment(
"promise_type is trivially destructible, no dtor required."sv));
684 mOutputFormatHelper.AppendNewLine();
685 mOutputFormatHelper.AppendCommentNewLine(
"This function invoked by coroutine_handle<>::destroy()"sv);
691 auto* deallocPromise =
AccessMember(mASTData.mFrameAccessDeclRef, dtorFuncDecl);
692 auto* dtorCall =
CallMemberFun(deallocPromise, GetFrameType());
693 deallocFuncBodyStmts.Add(dtorCall);
695 deallocFuncBodyStmts.Add(
Comment(
"Deallocating the coroutine frame"sv));
696 deallocFuncBodyStmts.Add(
697 Comment(
"Note: The actual argument to delete is __builtin_coro_frame with the promise as parameter"sv));
699 deallocFuncBodyStmts.Add(stmt->getDeallocate());
702 InsertArg(deallocFuncDecl);
709 SwitchStmt* sstmt =
Switch(mASTData.mSuspendIndexAccess);
712 auto* initialSuspendCase =
Case(0,
Break());
716 switchBodyStmts.
Add(
Case(i + 1,
Goto(BuildResumeLabelName(i + 1))));
720 sstmt->setBody(switchBody);
723 Comment(
"Create a switch to get to the correct resume point"sv), sstmt, stmt->getInitSuspendStmt()};
726 mState = eState::InitialSuspend;
728 if(mASTData.mThisExprs.size()) {
732 mInsertVarDecl =
false;
733 mSupressRecordDecls =
true;
735 for(
const auto* c : stmt->getBody()->children()) {
736 funcBodyStmts.Add(c);
739 if(
const auto* coReturnVoid = dyn_cast_or_null<CoreturnStmt>(stmt->getFallthroughHandler())) {
740 funcBodyStmts.Add(coReturnVoid);
744 funcBodyStmts.Add(gotoFinalSuspend);
746 auto* body = [&]() ->
Stmt* {
750 if(
const auto* exceptionHandler = stmt->getExceptionHandler()) {
753 auto* ifStmt =
If(
Not(mASTData.mInitialAwaitResumeCalledAccess),
Throw());
757 return Try(tryBody,
Catch(catchBodyStmts));
765 mOutputFormatHelper.AppendNewLine();
768 InsertArg(finalSuspendLabel);
769 mState = eState::FinalSuspend;
770 InsertArg(stmt->getFinalSuspendStmt());
773 mInsertVarDecl =
true;
779 if(not mSupressRecordDecls) {
789 InsertArg(stmt->getSubExpr());
800 if(
const auto* callee = dyn_cast_or_null<DeclRefExpr>(stmt->getCallee()->IgnoreCasts())) {
805 }
else if(
GetPlainName(*callee) ==
"__builtin_coro_free"sv) {
809 }
else if(
GetPlainName(*callee) ==
"__builtin_coro_size"sv) {
819 static std::optional<std::string>
820 FindValue(llvm::DenseMap<
const Expr*, std::pair<const DeclRefExpr*, std::string>>& map,
const Expr* key)
822 if(
const auto& s = map.find(key); s != map.end()) {
823 return s->second.second;
832 const auto* sourceExpr = stmt->getSourceExpr();
834 if(
const auto& s =
FindValue(mOpaqueValues, sourceExpr)) {
835 mOutputFormatHelper.Append(s.value());
850 if(
auto [thisDeref, v] = value; (thisDeref == dref) and (v == lookupName)) {
857 mOpaqueValues.insert(std::make_pair(sourceExpr, std::make_pair(dref, accessName)));
862 auto* promiseField =
AddField(name, stmt->getType());
863 BinaryOperator* assignPromiseSuspend =
864 Assign(mASTData.mFrameAccessDeclRef, promiseField, stmt->getSourceExpr());
866 codeGenerator.InsertArg(assignPromiseSuspend);
867 ofm.AppendSemiNewLine();
869 ofm.SetIndent(mOutputFormatHelper);
871 mOutputFormatHelper.InsertAt(mPosBeforeSuspendExpr, ofm);
872 mOutputFormatHelper.Append(accessName);
877 std::string CoroutinesCodeGenerator::BuildResumeLabelName(
int index)
const
885 mOutputFormatHelper.AppendNewLine();
886 InsertInstantiationPoint(
GetGlobalAST().getSourceManager(), stmt->getKeywordLoc(), [&] {
887 if(isa<CoawaitExpr>(stmt)) {
888 return kwCoAwaitSpace;
890 return kwCoYieldSpace;
894 mPosBeforeSuspendExpr = mOutputFormatHelper.CurrentPos();
913 mSupressCasts =
true;
915 auto* il =
Int32(++mSuspendsCount);
916 auto* bop =
Assign(mASTData.mSuspendIndexAccess, mASTData.mSuspendIndexField, il);
921 const bool returnsVoid{stmt->getSuspendExpr()->getType()->isVoidType()};
926 StmtsContainer bodyStmts{};
927 Expr* initializeInitialAwaitResume =
nullptr;
929 auto addInitialAwaitSuspendCalled = [&] {
932 if(eState::InitialSuspend == mState) {
933 mState = eState::Body;
935 initializeInitialAwaitResume =
936 Assign(mASTData.mFrameAccessDeclRef, mASTData.mInitialAwaitResumeCalledField,
Bool(
true));
937 bodyStmts.Add(initializeInitialAwaitResume);
942 bodyStmts.Add(stmt->getSuspendExpr());
943 addInitialAwaitSuspendCalled();
946 InsertArg(
If(
Not(stmt->getReadyExpr()), bodyStmts));
949 addInitialAwaitSuspendCalled();
952 auto* ifSuspend =
If(stmt->getSuspendExpr(), bodyStmts);
954 InsertArg(
If(
Not(stmt->getReadyExpr()), ifSuspend));
957 if(not returnsVoid and initializeInitialAwaitResume) {
959 InsertArgWithNull(initializeInitialAwaitResume);
960 mOutputFormatHelper.AppendNewLine();
963 auto* suspendLabel =
Label(BuildResumeLabelName(mSuspendsCount));
964 InsertArg(suspendLabel);
966 if(eState::FinalSuspend == mState) {
967 auto* memExpr =
AccessMember(mASTData.mFrameAccessDeclRef, mASTData.mDestroyFnField,
true);
968 auto* callCoroFSM =
Call(memExpr, {mASTData.mFrameAccessDeclRef});
969 InsertArg(callCoroFSM);
973 const auto* resumeExpr = stmt->getResumeExpr();
975 if(not resumeExpr->getType()->isVoidType()) {
976 const auto* sourceExpr = stmt->getOpaqueValue()->getSourceExpr();
978 if(
const auto& s =
FindValue(mOpaqueValues, sourceExpr)) {
982 AddField(fieldName, resumeExpr->getType());
986 InsertArg(resumeExpr);
994 if(stmt->getPromiseCall()) {
997 if(stmt->isImplicit()) {
1004 void CoroutinesCodeGenerator::InsertArgWithNull(
const Stmt* stmt)
const ASTContext & GetGlobalAST()
Get access to the ASTContext.
constexpr std::string_view hlpDestroyFn
constexpr std::string_view kwCoReturnSpace
constexpr std::string_view kwInternalThis
constexpr std::string_view hlpResumeFn
constexpr std::string_view hlpAssing
#define RETURN_IF(cond)
! A helper inspired by https://github.com/Microsoft/wil/wiki/Error-handling-helpers
virtual void InsertArg(const Decl *stmt)
void InsertInstantiationPoint(const SourceManager &sm, const SourceLocation &instLoc, std::string_view text={})
Inserts the instantiation point of a template.
void InsertTemplateArg(const TemplateArgument &arg)
OutputFormatHelper & mOutputFormatHelper
A special container which creates either a CodeGenerator or a CfrontCodeGenerator depending on the co...
A special generator for coroutines. It is only activated, if -show-coroutines-transformation is given...
virtual void InsertArg(const Decl *stmt)
void InsertCoroutine(const FunctionDecl &fd, const CoroutineBodyStmt *body)
~CoroutinesCodeGenerator() override
BinaryOperator * Equal(Expr *var, Expr *assignExpr)
UnaryOperator * AddrOf(const Expr *stmt)
CXXReinterpretCastExpr * ReinterpretCast(QualType toType, const Expr *toExpr, bool makePointer)
CXXRecordDecl * Struct(std::string_view name)
FieldDecl * mkFieldDecl(DeclContext *dc, std::string_view name, QualType type)
DeclRefExpr * mkDeclRefExpr(const ValueDecl *vd)
CXXNewExpr * New(ArrayRef< Expr * > placementArgs, const Expr *expr, QualType t)
std::vector< std::pair< std::string_view, QualType > > params_vector
CallExpr * Call(const FunctionDecl *fd, ArrayRef< Expr * > params)
UnaryOperator * Ref(const Expr *e)
VarDecl * Variable(std::string_view name, QualType type, DeclContext *dc)
MemberExpr * AccessMember(const Expr *expr, const ValueDecl *vd, bool isArrow)
DeclRefExpr * mkVarDeclRefExpr(std::string_view name, QualType type)
ReturnStmt * Return(Expr *stmt)
CaseStmt * Case(int value, Stmt *stmt)
Stmt * Comment(std::string_view comment)
IfStmt * If(const Expr *condition, ArrayRef< Stmt * > bodyStmts)
GotoStmt * Goto(std::string_view labelName)
CXXCatchStmt * Catch(ArrayRef< Stmt * > body)
CXXThrowExpr * Throw(const Expr *expr)
void ReplaceNode(Stmt *parent, Stmt *oldNode, Stmt *newNode)
UnaryExprOrTypeTraitExpr * Sizeof(QualType toType)
LabelStmt * Label(std::string_view name)
UnaryOperator * Not(const Expr *stmt)
CXXBoolLiteralExpr * Bool(bool b)
FunctionDecl * Function(std::string_view name, QualType returnType, const params_vector ¶meters)
CompoundStmt * mkCompoundStmt(ArrayRef< Stmt * > bodyStmts, SourceLocation beginLoc, SourceLocation endLoc)
SwitchStmt * Switch(Expr *stmt)
IntegerLiteral * Int32(uint64_t value)
ParmVarDecl * Parameter(const FunctionDecl *fd, std::string_view name, QualType type)
UnaryOperator * Dref(const Expr *stmt)
QualType Ptr(QualType srcType)
CXXStaticCastExpr * StaticCast(QualType toType, const Expr *toExpr, bool makePointer)
CXXMemberCallExpr * CallMemberFun(Expr *memExpr, QualType retType)
BinaryOperator * Assign(const VarDecl *var, Expr *assignExpr)
CXXTryStmt * Try(const Stmt *tryBody, CXXCatchStmt *catchAllBody)
const std::string SUSPEND_INDEX_NAME
static auto * CreateCoroFunctionDecl(std::string funcName, QualType type)
const std::string RESUME_LABEL_PREFIX
bool Contains(const std::string_view source, const std::string_view search)
const DeclRefExpr * FindDeclRef(const Stmt *stmt)
Go deep in a Stmt if necessary and look to all childs for a DeclRefExpr.
std::string GetPlainName(const DeclRefExpr &DRE)
static void SetFunctionBody(FunctionDecl *fd, StmtsContainer &bodyStmts)
constexpr std::string_view CORO_FRAME_NAME
std::string MakeLineColumnName(const SourceManager &sm, const SourceLocation &loc, const std::string_view &prefix)
std::string GetName(const NamedDecl &nd, const QualifiedName qualifiedName)
void ReplaceAll(std::string &str, std::string_view from, std::string_view to)
std::string BuildInternalVarName(const std::string_view &varName)
static FieldDecl * AddField(CoroutineASTData &astData, std::string_view name, QualType type)
static std::optional< std::string > FindValue(llvm::DenseMap< const Expr *, std::pair< const DeclRefExpr *, std::string >> &map, const Expr *key)
const std::string CORO_FRAME_ACCESS_THIS
std::string BuildTemplateParamObjectName(std::string name)
const std::string FINAL_SUSPEND_NAME
const std::string INITIAL_AWAIT_SUSPEND_CALLED_NAME
static std::string BuildSuspendVarName(const OpaqueValueExpr *stmt)
void EnableGlobalInsert(GlobalInserts idx)
const std::string CORO_FRAME_ACCESS
std::string StrCat(const auto &... args)
FieldDecl * mInitialAwaitResumeCalledField
std::vector< const CXXThisExpr * > mThisExprs
FieldDecl * mPromiseField
FieldDecl * mSuspendIndexField
MemberExpr * mSuspendIndexAccess
DeclRefExpr * mFrameAccessDeclRef
CXXRecordDecl * mFrameType
MemberExpr * mInitialAwaitResumeCalledAccess
! A helper type to have a container for ArrayRef
void Add(const Stmt *stmt)