llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang-codegen Author: None (Sirraide) <details> <summary>Changes</summary> --- Patch is 98.90 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/169680.diff 43 Files Affected: - (modified) clang/include/clang/AST/ASTNodeTraverser.h (+6) - (modified) clang/include/clang/AST/ComputeDependence.h (+3) - (modified) clang/include/clang/AST/Decl.h (+3-1) - (modified) clang/include/clang/AST/DeclBase.h (+13) - (modified) clang/include/clang/AST/DeclTemplate.h (+114) - (modified) clang/include/clang/AST/ExprCXX.h (+159) - (modified) clang/include/clang/AST/RecursiveASTVisitor.h (+17) - (modified) clang/include/clang/AST/StmtCXX.h (+478) - (modified) clang/include/clang/AST/TextNodeDumper.h (+5) - (modified) clang/include/clang/Basic/DeclNodes.td (+1) - (modified) clang/include/clang/Basic/StmtNodes.td (+14) - (modified) clang/include/clang/Serialization/ASTBitCodes.h (+31-10) - (modified) clang/lib/AST/ASTImporter.cpp (+174) - (modified) clang/lib/AST/ComputeDependence.cpp (+7) - (modified) clang/lib/AST/DeclBase.cpp (+13-1) - (modified) clang/lib/AST/DeclPrinter.cpp (+6) - (modified) clang/lib/AST/DeclTemplate.cpp (+23) - (modified) clang/lib/AST/Expr.cpp (+3) - (modified) clang/lib/AST/ExprCXX.cpp (+58) - (modified) clang/lib/AST/ExprClassification.cpp (+3) - (modified) clang/lib/AST/ExprConstant.cpp (+3) - (modified) clang/lib/AST/ItaniumMangle.cpp (+9-1) - (modified) clang/lib/AST/StmtCXX.cpp (+153) - (modified) clang/lib/AST/StmtPrinter.cpp (+61-1) - (modified) clang/lib/AST/StmtProfile.cpp (+47) - (modified) clang/lib/AST/TextNodeDumper.cpp (+22-1) - (modified) clang/lib/Sema/Sema.cpp (+4-3) - (modified) clang/lib/Sema/SemaDecl.cpp (+2-2) - (modified) clang/lib/Sema/SemaExceptionSpec.cpp (+8) - (modified) clang/lib/Sema/SemaExpr.cpp (+3-2) - (modified) clang/lib/Sema/SemaExprCXX.cpp (-1) - (modified) clang/lib/Sema/SemaLambda.cpp (+15-13) - (modified) clang/lib/Sema/SemaLookup.cpp (+3-1) - (modified) clang/lib/Sema/SemaTemplateInstantiateDecl.cpp (+5) - (modified) clang/lib/Sema/TreeTransform.h (+53) - (modified) clang/lib/Serialization/ASTCommon.cpp (+1) - (modified) clang/lib/Serialization/ASTReaderDecl.cpp (+12) - (modified) clang/lib/Serialization/ASTReaderStmt.cpp (+107) - (modified) clang/lib/Serialization/ASTWriterDecl.cpp (+9) - (modified) clang/lib/Serialization/ASTWriterStmt.cpp (+79) - (modified) clang/lib/StaticAnalyzer/Core/ExprEngine.cpp (+8) - (modified) clang/tools/libclang/CIndex.cpp (+1) - (modified) clang/tools/libclang/CXCursor.cpp (+8) ``````````diff diff --git a/clang/include/clang/AST/ASTNodeTraverser.h b/clang/include/clang/AST/ASTNodeTraverser.h index e74bb72571d64..fd64d86f83bfd 100644 --- a/clang/include/clang/AST/ASTNodeTraverser.h +++ b/clang/include/clang/AST/ASTNodeTraverser.h @@ -959,6 +959,12 @@ class ASTNodeTraverser } } + void VisitCXXExpansionStmtDecl(const CXXExpansionStmtDecl *Node) { + Visit(Node->getExpansionPattern()); + if (Traversal != TK_IgnoreUnlessSpelledInSource) + Visit(Node->getInstantiations()); + } + void VisitCallExpr(const CallExpr *Node) { for (const auto *Child : make_filter_range(Node->children(), [this](const Stmt *Child) { diff --git a/clang/include/clang/AST/ComputeDependence.h b/clang/include/clang/AST/ComputeDependence.h index c298f2620f211..792f45bea5aeb 100644 --- a/clang/include/clang/AST/ComputeDependence.h +++ b/clang/include/clang/AST/ComputeDependence.h @@ -94,6 +94,7 @@ class DesignatedInitExpr; class ParenListExpr; class PseudoObjectExpr; class AtomicExpr; +class CXXExpansionInitListExpr; class ArraySectionExpr; class OMPArrayShapingExpr; class OMPIteratorExpr; @@ -191,6 +192,8 @@ ExprDependence computeDependence(ParenListExpr *E); ExprDependence computeDependence(PseudoObjectExpr *E); ExprDependence computeDependence(AtomicExpr *E); +ExprDependence computeDependence(CXXExpansionInitListExpr *E); + ExprDependence computeDependence(ArraySectionExpr *E); ExprDependence computeDependence(OMPArrayShapingExpr *E); ExprDependence computeDependence(OMPIteratorExpr *E); diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index ee2321dd158d4..b8f8e002ebcce 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -1254,7 +1254,9 @@ class VarDecl : public DeclaratorDecl, public Redeclarable<VarDecl> { if (getKind() != Decl::Var && getKind() != Decl::Decomposition) return false; if (const DeclContext *DC = getLexicalDeclContext()) - return DC->getRedeclContext()->isFunctionOrMethod(); + return DC->getEnclosingNonExpansionStatementContext() + ->getRedeclContext() + ->isFunctionOrMethod(); return false; } diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h index 5519787d71f88..71e6898f4c94d 100644 --- a/clang/include/clang/AST/DeclBase.h +++ b/clang/include/clang/AST/DeclBase.h @@ -2195,6 +2195,10 @@ class DeclContext { return getDeclKind() == Decl::RequiresExprBody; } + bool isExpansionStmt() const { + return getDeclKind() == Decl::CXXExpansionStmt; + } + bool isNamespace() const { return getDeclKind() == Decl::Namespace; } bool isStdNamespace() const; @@ -2292,6 +2296,15 @@ class DeclContext { return const_cast<DeclContext *>(this)->getOuterLexicalRecordContext(); } + /// Retrieve the innermost enclosing context that doesn't belong to an + /// expansion statement. Returns 'this' if this context is not an expansion + /// statement. + DeclContext *getEnclosingNonExpansionStatementContext(); + const DeclContext *getEnclosingNonExpansionStatementContext() const { + return const_cast<DeclContext *>(this) + ->getEnclosingNonExpansionStatementContext(); + } + /// Test if this context is part of the enclosing namespace set of /// the context NS, as defined in C++0x [namespace.def]p9. If either context /// isn't a namespace, this is equivalent to Equals(). diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h index a4a1bb9c13c79..45a58b6589074 100644 --- a/clang/include/clang/AST/DeclTemplate.h +++ b/clang/include/clang/AST/DeclTemplate.h @@ -3343,6 +3343,120 @@ class TemplateParamObjectDecl : public ValueDecl, static bool classofKind(Kind K) { return K == TemplateParamObject; } }; +/// Represents a C++26 expansion statement declaration. +/// +/// This is a bit of a hack, since expansion statements shouldn't really be +/// 'declarations' per se (they don't declare anything). Nevertheless, we *do* +/// need them to be declaration *contexts*, because the DeclContext is used to +/// compute the 'template depth' of entities enclosed therein. In particular, +/// the 'template depth' is used to find instantiations of parameter variables, +/// and a lambda enclosed within an expansion statement cannot compute its +/// template depth without a pointer to the enclosing expansion statement. +/// +/// For the remainder of this comment, let 'expanding' an expansion statement +/// refer to the process of performing template substitution on its body N +/// times, where N is the expansion size (how this size is determined depends on +/// the kind of expansion statement); by contrast we may sometimes 'instantiate' +/// an expansion statement (because it happens to be in a template). This is +/// just regular template instantiation. +/// +/// Apart from a template parameter list that contains a template parameter used +/// as the expansion index, this node contains a 'CXXExpansionStmtPattern' as +/// well as a 'CXXExpansionStmtInstantiation'. These two members correspond to +/// distinct representations of the expansion statement: the former is used +/// prior to expansion and contains all the parts needed to perform expansion; +/// the latter holds the expanded/desugared AST nodes that result from the +/// expansion. +/// +/// After expansion, the 'CXXExpansionStmtPattern' is no longer updated and left +/// as-is; this also means that, if an already-expanded expansion statement is +/// inside a template, and that template is then instantiated, the +/// 'CXXExpansionStmtPattern' is *not* instantiated; only the +/// 'CXXExpansionStmtInstantiation' is. The latter is also what's used for +/// codegen and constant evaluation. +/// +/// For example, if the user writes the following expansion statement: +/// \verbatim +/// std::tuple<int, int, int> a{1, 2, 3}; +/// template for (auto x : a) { +/// // ... +/// } +/// \endverbatim +/// +/// The 'CXXExpansionStmtPattern' of this particular 'CXXExpansionStmtDecl' is a +/// 'CXXDestructuringExpansionStmtPattern', which stores, amongst other things, +/// the declaration of the variable 'x' as well as the expansion-initializer +/// 'a'. +/// +/// After expansion, we end up with a 'CXXExpansionStmtInstantiation' that +/// contains a DecompositionDecl and 3 CompoundStmts, one for each expansion: +/// +/// \verbatim +/// { +/// auto [__u0, __u1, __u2] = a; +/// { +/// auto x = __u0; +/// // ... +/// } +/// { +/// auto x = __u1; +/// // ... +/// } +/// { +/// auto x = __u2; +/// // ... +/// } +/// } +/// \endverbatim +/// +/// The outer braces shown above are implicit; we don't actually create another +/// CompoundStmt wrapping everything. +/// +/// \see CXXExpansionStmtPattern +/// \see CXXExpansionStmtInstantiation +class CXXExpansionStmtDecl : public Decl, public DeclContext { + CXXExpansionStmtPattern *Expansion = nullptr; + TemplateParameterList *TParams; + CXXExpansionStmtInstantiation *Instantiations = nullptr; + + CXXExpansionStmtDecl(DeclContext *DC, SourceLocation Loc, + TemplateParameterList *TParams); + +public: + friend class ASTDeclReader; + + static CXXExpansionStmtDecl *Create(ASTContext &C, DeclContext *DC, + SourceLocation Loc, + TemplateParameterList *TParams); + static CXXExpansionStmtDecl *CreateDeserialized(ASTContext &C, + GlobalDeclID ID); + + CXXExpansionStmtPattern *getExpansionPattern() { return Expansion; } + const CXXExpansionStmtPattern *getExpansionPattern() const { + return Expansion; + } + void setExpansionPattern(CXXExpansionStmtPattern *S) { Expansion = S; } + + CXXExpansionStmtInstantiation *getInstantiations() { return Instantiations; } + const CXXExpansionStmtInstantiation *getInstantiations() const { + return Instantiations; + } + + void setInstantiations(CXXExpansionStmtInstantiation *S) { + Instantiations = S; + } + + NonTypeTemplateParmDecl *getIndexTemplateParm() const { + return cast<NonTypeTemplateParmDecl>(TParams->getParam(0)); + } + TemplateParameterList *getTemplateParameters() const { return TParams; } + + SourceRange getSourceRange() const override LLVM_READONLY; + + static bool classof(const Decl *D) { return classofKind(D->getKind()); } + static bool classofKind(Kind K) { return K == CXXExpansionStmt; } +}; + inline NamedDecl *getAsNamedDecl(TemplateParameter P) { if (auto *PD = P.dyn_cast<TemplateTypeParmDecl *>()) return PD; diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h index 9435ab069a520..668a51dad0ae9 100644 --- a/clang/include/clang/AST/ExprCXX.h +++ b/clang/include/clang/AST/ExprCXX.h @@ -5499,6 +5499,165 @@ class BuiltinBitCastExpr final } }; +/// Represents an expansion-init-list of an enumerating expansion statement. +/// +/// \see CXXEnumeratingExpansionStmtPattern +class CXXExpansionInitListExpr final + : public Expr, + llvm::TrailingObjects<CXXExpansionInitListExpr, Expr *> { + friend class ASTStmtReader; + friend TrailingObjects; + + const unsigned NumExprs; + SourceLocation LBraceLoc; + SourceLocation RBraceLoc; + + CXXExpansionInitListExpr(EmptyShell ES, unsigned NumExprs); + CXXExpansionInitListExpr(ArrayRef<Expr *> Exprs, SourceLocation LBraceLoc, + SourceLocation RBraceLoc); + +public: + static CXXExpansionInitListExpr *Create(const ASTContext &C, + ArrayRef<Expr *> Exprs, + SourceLocation LBraceLoc, + SourceLocation RBraceLoc); + + static CXXExpansionInitListExpr * + CreateEmpty(const ASTContext &C, EmptyShell Empty, unsigned NumExprs); + + ArrayRef<Expr *> getExprs() const { return getTrailingObjects(NumExprs); } + MutableArrayRef<Expr *> getExprs() { return getTrailingObjects(NumExprs); } + unsigned getNumExprs() const { return NumExprs; } + + bool containsPackExpansion() const; + + SourceLocation getBeginLoc() const { return getLBraceLoc(); } + SourceLocation getEndLoc() const { return getRBraceLoc(); } + + SourceLocation getLBraceLoc() const { return LBraceLoc; } + SourceLocation getRBraceLoc() const { return RBraceLoc; } + + child_range children() { + const_child_range CCR = + const_cast<const CXXExpansionInitListExpr *>(this)->children(); + return child_range(cast_away_const(CCR.begin()), + cast_away_const(CCR.end())); + } + + const_child_range children() const { + Stmt **Stmts = getTrailingStmts(); + return const_child_range(Stmts, Stmts + NumExprs); + } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == CXXExpansionInitListExprClass; + } + +private: + Stmt **getTrailingStmts() const { + return reinterpret_cast<Stmt **>(const_cast<Expr **>(getTrailingObjects())); + } +}; + +/// Helper that selects an expression from an expansion init list depending +/// on the current expansion index. +/// +/// \see CXXEnumeratingExpansionStmtPattern +class CXXExpansionInitListSelectExpr : public Expr { + friend class ASTStmtReader; + + enum SubExpr { RANGE, INDEX, COUNT }; + Expr *SubExprs[COUNT]; + +public: + CXXExpansionInitListSelectExpr(EmptyShell Empty); + CXXExpansionInitListSelectExpr(const ASTContext &C, + CXXExpansionInitListExpr *Range, Expr *Idx); + + CXXExpansionInitListExpr *getRangeExpr() { + return cast<CXXExpansionInitListExpr>(SubExprs[RANGE]); + } + + const CXXExpansionInitListExpr *getRangeExpr() const { + return cast<CXXExpansionInitListExpr>(SubExprs[RANGE]); + } + + void setRangeExpr(CXXExpansionInitListExpr *E) { SubExprs[RANGE] = E; } + + Expr *getIndexExpr() { return SubExprs[INDEX]; } + const Expr *getIndexExpr() const { return SubExprs[INDEX]; } + void setIndexExpr(Expr *E) { SubExprs[INDEX] = E; } + + SourceLocation getBeginLoc() const { return getRangeExpr()->getBeginLoc(); } + SourceLocation getEndLoc() const { return getRangeExpr()->getEndLoc(); } + + child_range children() { + return child_range(reinterpret_cast<Stmt **>(SubExprs), + reinterpret_cast<Stmt **>(SubExprs + COUNT)); + } + + const_child_range children() const { + return const_child_range( + reinterpret_cast<Stmt **>(const_cast<Expr **>(SubExprs)), + reinterpret_cast<Stmt **>(const_cast<Expr **>(SubExprs + COUNT))); + } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == CXXExpansionInitListSelectExprClass; + } +}; + +/// This class serves the same purpose as CXXExpansionInitListSelectExpr, but +/// for destructuring expansion statements; that is, instead of selecting among +/// a list of expressions, it selects from a list of 'BindingDecl's. +/// +/// \see CXXEnumeratingExpansionStmtPattern +/// \see CXXDestructuringExpansionStmtPattern +class CXXDestructuringExpansionSelectExpr : public Expr { + friend class ASTStmtReader; + + DecompositionDecl *Decomposition; + Expr *Index; + +public: + CXXDestructuringExpansionSelectExpr(EmptyShell Empty); + CXXDestructuringExpansionSelectExpr(const ASTContext &C, + DecompositionDecl *Decomposition, + Expr *Index); + + DecompositionDecl *getDecompositionDecl() { + return cast<DecompositionDecl>(Decomposition); + } + + const DecompositionDecl *getDecompositionDecl() const { + return cast<DecompositionDecl>(Decomposition); + } + + void setDecompositionDecl(DecompositionDecl *E) { Decomposition = E; } + + Expr *getIndexExpr() { return Index; } + const Expr *getIndexExpr() const { return Index; } + void setIndexExpr(Expr *E) { Index = E; } + + SourceLocation getBeginLoc() const { return Decomposition->getBeginLoc(); } + SourceLocation getEndLoc() const { return Decomposition->getEndLoc(); } + + child_range children() { + return child_range(reinterpret_cast<Stmt **>(&Index), + reinterpret_cast<Stmt **>(&Index + 1)); + } + + const_child_range children() const { + return const_child_range( + reinterpret_cast<Stmt **>(const_cast<Expr **>(&Index)), + reinterpret_cast<Stmt **>(const_cast<Expr **>(&Index + 1))); + } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == CXXDestructuringExpansionSelectExprClass; + } +}; + } // namespace clang #endif // LLVM_CLANG_AST_EXPRCXX_H diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index 8f427427d71ed..24052df70c7a8 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -1881,6 +1881,14 @@ DEF_TRAVERSE_DECL(UsingShadowDecl, {}) DEF_TRAVERSE_DECL(ConstructorUsingShadowDecl, {}) +DEF_TRAVERSE_DECL(CXXExpansionStmtDecl, { + if (D->getInstantiations() && + getDerived().shouldVisitTemplateInstantiations()) + TRY_TO(TraverseStmt(D->getInstantiations())); + + TRY_TO(TraverseStmt(D->getExpansionPattern())); +}) + DEF_TRAVERSE_DECL(OMPThreadPrivateDecl, { for (auto *I : D->varlist()) { TRY_TO(TraverseStmt(I)); @@ -3117,6 +3125,15 @@ DEF_TRAVERSE_STMT(RequiresExpr, { TRY_TO(TraverseConceptRequirement(Req)); }) +DEF_TRAVERSE_STMT(CXXEnumeratingExpansionStmtPattern, {}) +DEF_TRAVERSE_STMT(CXXIteratingExpansionStmtPattern, {}) +DEF_TRAVERSE_STMT(CXXDestructuringExpansionStmtPattern, {}) +DEF_TRAVERSE_STMT(CXXDependentExpansionStmtPattern, {}) +DEF_TRAVERSE_STMT(CXXExpansionStmtInstantiation, {}) +DEF_TRAVERSE_STMT(CXXExpansionInitListExpr, {}) +DEF_TRAVERSE_STMT(CXXExpansionInitListSelectExpr, {}) +DEF_TRAVERSE_STMT(CXXDestructuringExpansionSelectExpr, {}) + // These literals (all of them) do not need any action. DEF_TRAVERSE_STMT(IntegerLiteral, {}) DEF_TRAVERSE_STMT(FixedPointLiteral, {}) diff --git a/clang/include/clang/AST/StmtCXX.h b/clang/include/clang/AST/StmtCXX.h index 5d68d3ef64a20..96c3f912d6c3e 100644 --- a/clang/include/clang/AST/StmtCXX.h +++ b/clang/include/clang/AST/StmtCXX.h @@ -22,6 +22,7 @@ namespace clang { class VarDecl; +class CXXExpansionStmtDecl; /// CXXCatchStmt - This represents a C++ catch block. /// @@ -524,6 +525,483 @@ class CoreturnStmt : public Stmt { } }; +/// CXXExpansionStmtPattern - Base class for an unexpanded C++ expansion +/// statement. +/// +/// The main purpose for this class is to store the AST nodes common to all +/// variants of expansion statements; it also provides storage for additional +/// subexpressions required by its derived classes. This is to simplify the +/// implementation of 'children()' and friends. +/// +/// \see CXXExpansionStmtDecl +/// \see CXXEnumeratingExpansionStmtPattern +/// \see CXXIteratingExpansionStmtPattern +/// \see CXXDestructuringExpansionStmtPattern +/// \see CXXDependentExpansionStmtPattern +class CXXExpansionStmtPattern : public Stmt { + friend class ASTStmtReader; + + CXXExpansionStmtDecl *ParentDecl; + SourceLocation LParenLoc; + SourceLocation ColonLoc; + SourceLocation RParenLoc; + +protected: + enum SubStmt { + INIT, + VAR, + BODY, + FIRST_CHILD_STMT, + + // CXXDependentExpansionStmtPattern + EXPANSION_INITIALIZER = FIRST_CHILD_STMT, + COUNT_CXXDependentExpansionStmtPattern, + + // CXXDestructuringExpansionStmtPattern + DECOMP_DECL = FIRST_CHILD_STMT, + COUNT_CXXDestructuringExpansionStmtPattern, + + // CXXIteratingExpansionStmtPattern + RANGE = FIRST_CHILD_STMT, + BEGIN, + END, + COUNT_CXXIteratingExpansionStmtPattern, + + MAX_COUNT = COUNT_CXXIteratingExpansionStmtPattern, + }; + + // Managing the memory for this properly would be rather complicated, and + // expansion statements are fairly uncommon, so just allocate space for the + // maximum amount of substatements we could possibly have. + Stmt *SubStmts[MAX_COUNT]; + + CXXExpansionStmtPattern(StmtClass SC, EmptyShell Empty); + CXXExpansionStmtPattern(StmtClass SC, CXXExpansionStmtDecl *ESD, Stmt *Init, + DeclStmt *ExpansionVar, SourceLocation LParenLoc, + SourceLocation ColonLoc, SourceLocation RParenLoc); + +public: + SourceLocation getLParenLoc() const { return LParenLoc; } + SourceLocation getColonLoc() const { return ColonLoc; } + SourceLocation getRParenLoc() const { return RParenLoc; } + + SourceLocation getBeginLoc() const; + SourceLocation getEndLoc() const { + return getBody() ? getBody()->getEndLoc() : RParenLoc; + } + + bool hasDependentSize() const; + + CXXExpansionStmtDecl *getDecl() { return ParentDecl; } + const CXXExpansionStmtDecl *getDecl() const { return ParentDecl; } + + Stmt *getInit() { return SubStmts[INIT]; } + const Stmt *getInit() const { return SubStmts[INIT]; } + void setInit(Stmt *S) { SubStmts[INIT] = S; } + + VarDecl *getExpansionVariable(); + const VarDecl *getExpansionVariable() const { + return const_cast<CXXExpansionStmtPattern *>(this)->getExpansionVariable(); + } + + DeclStmt *getExpansionVarStmt() { return cast<DeclStmt>(SubStmts[VAR]); } + const DeclStmt *getExpansionVarStmt() const { + return cast<DeclStmt>(SubStmts[VAR]); + } + + void setExpansionVarStmt(Stmt *S) { SubStmts[VAR] = S; } + + Stmt *getBody() { return SubStmts[BODY]; } + const Stmt *getBody() const { return SubStmts[BODY]; } + void setBody(Stmt *S) { SubStmts[BODY] = S; } + + static bool classof(const Stmt *T) { + return T->getStmtClass() >= firstCXXExpansionStmtPatternConstant && + T->getStmtClass() <= lastCXXExpansionStmtPatternConstant; + } + + child_range children() { + return child_range(SubStmts, SubStmts + FIRST_CHILD_STMT); + } + + const_child_range children() const { + return const_child_range(SubStmts, SubStmts + FIRST_CHILD_STMT); + } +}; + +/// Represents an unexpanded enumerating expansion statement. +/// +/// An 'enumerating' expansion statement is one whose expansion-initializer +/// is a brace-enclosed expression-list; this list is syntactically similar to +/// an initializer list, but it isn't actually an expression in and of itself +/// (in that it is never evaluated or emitted) and instead is just treated as +/// a group of expressions. The expansion initializer of this is always a +/// 'CXXExpa... [truncated] `````````` </details> https://github.com/llvm/llvm-project/pull/169680 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
