https://github.com/Sirraide updated https://github.com/llvm/llvm-project/pull/169680
>From c41aa76173c252de8fc11413e07872ba96448856 Mon Sep 17 00:00:00 2001 From: Sirraide <[email protected]> Date: Tue, 25 Nov 2025 17:18:05 +0100 Subject: [PATCH] [Clang] [C++26] Expansion Statements (Part 1) --- clang/include/clang/AST/ASTNodeTraverser.h | 6 + clang/include/clang/AST/Decl.h | 6 +- clang/include/clang/AST/DeclBase.h | 13 + clang/include/clang/AST/DeclTemplate.h | 125 ++++ clang/include/clang/AST/ExprCXX.h | 44 ++ clang/include/clang/AST/RecursiveASTVisitor.h | 12 + clang/include/clang/AST/StmtCXX.h | 576 ++++++++++++++++++ clang/include/clang/AST/TextNodeDumper.h | 3 + clang/include/clang/Basic/DeclNodes.td | 1 + clang/include/clang/Basic/StmtNodes.td | 8 + clang/include/clang/Sema/Scope.h | 18 + .../include/clang/Serialization/ASTBitCodes.h | 10 + clang/lib/AST/ASTImporter.cpp | 120 ++++ clang/lib/AST/DeclBase.cpp | 14 +- clang/lib/AST/DeclPrinter.cpp | 6 + clang/lib/AST/DeclTemplate.cpp | 23 + clang/lib/AST/Expr.cpp | 1 + clang/lib/AST/ExprCXX.cpp | 12 + clang/lib/AST/ExprClassification.cpp | 1 + clang/lib/AST/ExprConstant.cpp | 1 + clang/lib/AST/ItaniumMangle.cpp | 8 +- clang/lib/AST/StmtCXX.cpp | 157 +++++ clang/lib/AST/StmtPrinter.cpp | 35 +- clang/lib/AST/StmtProfile.cpp | 16 + clang/lib/AST/TextNodeDumper.cpp | 32 +- clang/lib/CodeGen/CGDecl.cpp | 3 + clang/lib/CodeGen/CGStmt.cpp | 4 + clang/lib/Sema/IdentifierResolver.cpp | 7 +- clang/lib/Sema/Scope.cpp | 1 + clang/lib/Sema/Sema.cpp | 7 +- clang/lib/Sema/SemaDecl.cpp | 4 +- clang/lib/Sema/SemaExceptionSpec.cpp | 8 + clang/lib/Sema/SemaExpr.cpp | 5 +- clang/lib/Sema/SemaExprCXX.cpp | 1 - clang/lib/Sema/SemaLambda.cpp | 28 +- clang/lib/Sema/SemaLookup.cpp | 4 +- clang/lib/Sema/SemaStmtAttr.cpp | 12 + .../lib/Sema/SemaTemplateInstantiateDecl.cpp | 5 + clang/lib/Sema/TreeTransform.h | 18 + clang/lib/Serialization/ASTCommon.cpp | 1 + clang/lib/Serialization/ASTReaderDecl.cpp | 12 + clang/lib/Serialization/ASTReaderStmt.cpp | 47 ++ clang/lib/Serialization/ASTWriterDecl.cpp | 9 + clang/lib/Serialization/ASTWriterStmt.cpp | 33 + clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 3 + clang/tools/libclang/CIndex.cpp | 1 + clang/tools/libclang/CXCursor.cpp | 3 + 47 files changed, 1435 insertions(+), 29 deletions(-) diff --git a/clang/include/clang/AST/ASTNodeTraverser.h b/clang/include/clang/AST/ASTNodeTraverser.h index 3be24ff868c2d..c999b79c3b2a7 100644 --- a/clang/include/clang/AST/ASTNodeTraverser.h +++ b/clang/include/clang/AST/ASTNodeTraverser.h @@ -972,6 +972,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/Decl.h b/clang/include/clang/AST/Decl.h index 076d9ba935583..0224cf5a3612b 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -1262,14 +1262,16 @@ class VarDecl : public DeclaratorDecl, public Redeclarable<VarDecl> { /// Returns true for local variable declarations other than parameters. /// Note that this includes static variables inside of functions. It also - /// includes variables inside blocks. + /// includes variables inside blocks and expansion statements. /// /// void foo() { int x; static int y; extern int z; } bool isLocalVarDecl() const { 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..3a416f83d1d8f 100644 --- a/clang/include/clang/AST/DeclTemplate.h +++ b/clang/include/clang/AST/DeclTemplate.h @@ -3343,6 +3343,131 @@ 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. +/// 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. +/// +/// 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. +/// +/// Additionally, there is a 'NonTypeTemplateParmDecl', which is a template +/// parameter that serves as the expansion index, e.g. during the N-th +/// expansion, it is set to 'N'. See the documentation of +/// 'CXXExpansionStmtPattern', for more information on how this is used. +/// +/// 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. +/// +/// There are different kinds of expansion statements; see the comment on +/// 'CXXExpansionStmtPattern' for more information. +/// +/// As an 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' +/// 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 +/// is *equivalent* to the AST shown below. Note that only the inner '{}' (i.e. +/// those marked as 'Actual "CompoundStmt"' below) are actually present as +/// 'CompoundStmt's in the AST; the outer braces that wrap everything do *not* +/// correspond to an actual 'CompoundStmt' and are implicit in the sense that we +/// simply push a scope when evaluating or emitting IR for a +/// 'CXXExpansionStmtInstantiation'. +/// +/// \verbatim +/// { // Not actually present in the AST. +/// auto [__u0, __u1, __u2] = a; +/// { // Actual 'CompoundStmt'. +/// auto x = __u0; +/// // ... +/// } +/// { // Actual 'CompoundStmt'. +/// auto x = __u1; +/// // ... +/// } +/// { // Actual 'CompoundStmt'. +/// auto x = __u2; +/// // ... +/// } +/// } +/// \endverbatim +/// +/// See the documentation around 'CXXExpansionStmtInstantiation' for more notes +/// as to why this node exist and how it is used. +/// +/// \see CXXExpansionStmtPattern +/// \see CXXExpansionStmtInstantiation +class CXXExpansionStmtDecl : public Decl, public DeclContext { + CXXExpansionStmtPattern *Pattern = nullptr; + NonTypeTemplateParmDecl *IndexNTTP = nullptr; + CXXExpansionStmtInstantiation *Instantiations = nullptr; + + CXXExpansionStmtDecl(DeclContext *DC, SourceLocation Loc, + NonTypeTemplateParmDecl *NTTP); + +public: + friend class ASTDeclReader; + + static CXXExpansionStmtDecl *Create(ASTContext &C, DeclContext *DC, + SourceLocation Loc, + NonTypeTemplateParmDecl *NTTP); + static CXXExpansionStmtDecl *CreateDeserialized(ASTContext &C, + GlobalDeclID ID); + + CXXExpansionStmtPattern *getExpansionPattern() { return Pattern; } + const CXXExpansionStmtPattern *getExpansionPattern() const { + return Pattern; + } + void setExpansionPattern(CXXExpansionStmtPattern *S) { Pattern = S; } + + CXXExpansionStmtInstantiation *getInstantiations() { return Instantiations; } + const CXXExpansionStmtInstantiation *getInstantiations() const { + return Instantiations; + } + + void setInstantiations(CXXExpansionStmtInstantiation *S) { + Instantiations = S; + } + + NonTypeTemplateParmDecl *getIndexTemplateParm() { return IndexNTTP; } + const NonTypeTemplateParmDecl *getIndexTemplateParm() const { + return IndexNTTP; + } + + 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 c40cd929b7408..44f2559062943 100644 --- a/clang/include/clang/AST/ExprCXX.h +++ b/clang/include/clang/AST/ExprCXX.h @@ -5554,6 +5554,50 @@ class CXXReflectExpr : public Expr { } }; +/// Helper that selects an expression from an InitListExpr depending on the +/// current expansion index. See 'CXXExpansionStmtPattern' for how this is used. +class CXXExpansionSelectExpr : public Expr { + friend class ASTStmtReader; + + enum SubExpr { RANGE, INDEX, COUNT }; + Expr *SubExprs[COUNT]; + +public: + CXXExpansionSelectExpr(EmptyShell Empty); + CXXExpansionSelectExpr(const ASTContext &C, InitListExpr *Range, Expr *Idx); + + InitListExpr *getRangeExpr() { + return cast<InitListExpr>(SubExprs[RANGE]); + } + + const InitListExpr *getRangeExpr() const { + return cast<InitListExpr>(SubExprs[RANGE]); + } + + void setRangeExpr(InitListExpr *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() == CXXExpansionSelectExprClass; + } +}; } // 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 ce6ad723191e0..eafbe0f6c23ad 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -1887,6 +1887,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)); @@ -3136,6 +3144,10 @@ DEF_TRAVERSE_STMT(RequiresExpr, { TRY_TO(TraverseConceptRequirement(Req)); }) +DEF_TRAVERSE_STMT(CXXExpansionStmtPattern, {}) +DEF_TRAVERSE_STMT(CXXExpansionStmtInstantiation, {}) +DEF_TRAVERSE_STMT(CXXExpansionSelectExpr, {}) + // 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..7c955dd6091c5 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,581 @@ class CoreturnStmt : public Stmt { } }; +/// CXXExpansionStmtPattern - Represents an unexpanded C++ expansion statement. +/// +/// There are four kinds of expansion statements. +/// +/// 1. Enumerating expansion statements. +/// 2. Iterating expansion statements. +/// 3. Destructuring expansion statements. +/// 4. Dependent expansion statements. +/// +/// 1. 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 +/// syntactic-form 'InitListExpr'. +/// +/// Example: +/// \verbatim +/// template for (auto x : { 1, 2, 3 }) { +/// // ... +/// } +/// \endverbatim +/// +/// Note that the expression-list may also contain pack expansions, e.g. +/// '{ 1, xs... }', in which case the expansion size is dependent. +/// +/// Here, the '{ 1, 2, 3 }' is parsed as an 'InitListExpr'. This node +/// handles storing (and pack-expanding) the individual expressions. +/// +/// Sema then wraps this with a 'CXXExpansionSelectExpr', which also +/// contains a reference to an integral NTTP that is used as the expansion +/// index; this index is either dependent (if the expansion-size is dependent), +/// or set to a value of I in the I-th expansion during the expansion process. +/// +/// The actual expansion is done by 'BuildCXXExpansionSelectExpr()': for +/// example, during the 2nd expansion of '{ a, b, c }', I is equal to 1, and +/// BuildCXXExpansionSelectExpr(), when called via TreeTransform, +/// 'instantiates' the expression '{ a, b, c }' to just 'b'. +/// +/// 2. Represents an unexpanded iterating expansion statement. +/// +/// An 'iterating' expansion statement is one whose expansion-initializer is a +/// a range, i.e. it has a corresponding 'begin()'/'end()' pair that is +/// determined based on a number of conditions as stated in [stmt.expand] and +/// [stmt.ranged]. +/// +/// Specifically, let E denote the expansion-initializer; the expansion +/// statement is iterating if the type of E is not an array type, and either +/// +/// 2a. 'E.begin' and 'E.end' *exist* (irrespective of whether they're +/// accessible, deleted, or even callable), or +/// +/// 2b. ADL for 'begin(E)' and 'end(E)' finds at least one viable function. +/// +/// If neither A nor B apply to E (or if E is an array type), we treat this as +/// a destructuring expansion statement instead (see case 3 below). +/// +/// Notably, case 2a only checks whether the 'begin' and 'end' members exist and +/// does *not* perform proper overload resolution; this is because if there is +/// a begin/end function, but it for some reason is not usable (e.g. because it +/// is non-const but E is const), then we'd rather error and tell the user that +/// their begin/end function is wrong rather than falling back to destructuring. +/// +/// Conversely, case 2b *does* perform overload resolution, simply because ADL +/// may find quite a few begin/end overloads for unrelated types that happen to +/// be in the same namespace. E.g. if the type of E is 'std::tuple', then there +/// are quite a few begin/end pairs in the namespace 'std', but non of them can +/// actually be used for a 'std::tuple', and we definitely want to destructure a +/// tuple rather than error about it not being iterable. +/// +/// In either case, once we've decided that the expansion statement is indeed +/// iterating, we *do* make sure that the expression 'E.begin()'/'begin(E)' is +/// well-formed, but any error at that point is a hard error and does not make +/// us switch to destructuring instead. +/// +/// The result of this expression is stored in a variable 'begin', which is then +/// used to compute another variable 'iter' (which is just 'begin' + the +/// expansion index) during expansion. During the N-th expansion, the expansion +/// variable is then set to '*iter'. See [stmt.expand] for more information. +/// +/// The expression used to compute the size of the expansion is not stored and +/// is only created at the moment of expansion. See Sema::ComputeExpansionSize() +/// for more information about this. +/// +/// Example: +/// \verbatim +/// static constexpr std::string_view foo = "abcd"; +/// template for (auto x : foo) { +/// // ... +/// } +/// \endverbatim +/// +/// Here, 'begin' is 'foo.begin()', and during e.g. the 0-th expansion, 'iter' +/// is 'begin + 0', and thus '*iter' yields 'a', which results in 'x' being +/// a variable of type 'char' with value 'a'. +/// +/// 3. Represents an unexpanded destructuring expansion statement. +/// +/// A 'destructuring' expansion statement is any expansion statement that is +/// not enumerating or iterating (i.e. destructuring is the last thing we try, +/// and if it doesn't work, the program is ill-formed). +/// +/// This essentially involves treating the expansion-initializer as the +/// initializer of a structured-binding declaration, with the number of +/// bindings and expansion size determined by the usual means (array size, +/// std::tuple_size, etc.). +/// +/// During the N-th expansion, the expansion variable is then initialized with +/// the N-th binding of the structured-binding declaration. This is implemented +/// by wrapping the initializer with a CXXExpansionSelectExpr, which selects a +/// binding based on the current expansion index when called from TreeTransform. +/// +/// Example: +/// \verbatim +/// std::tuple<int, long, unsigned> a {1, 2l, 3u}; +/// template for (auto x : a) { +/// // ... +/// } +/// \endverbatim +/// +/// Here, we build 'auto [_U0, _U1, _U2] = a', and during e.g. the 0-th +/// expansion, 'x' is initialized with '_U0'. +/// +/// 4. Represents an expansion statement whose expansion-initializer is +/// type-dependent. +/// +/// This will eventually become an iterating or destructuring expansion +/// statement once the expansion-initializer is no longer dependent. +/// +/// Dependent expansion statements can never be enumerating: even if the +/// expansion size of an enumerating expansion statement is dependent (which +/// is possible if the expression-list contains a pack), we still don't build +/// an 'Enumerating' 'CXXExpansionStmtPattern' for it. +/// +/// Example: +/// \verbatim +/// template <typename T> +/// void f() { +/// template for (auto x : T()) { +/// // ... +/// } +/// } +/// \endverbatim +/// +/// \see CXXExpansionStmtDecl for more documentation on expansion statements. +class CXXExpansionStmtPattern final + : public Stmt, + llvm::TrailingObjects<CXXExpansionStmtPattern, Stmt *> { + friend class ASTStmtReader; + friend TrailingObjects; + +public: + enum class ExpansionStmtKind : uint8_t { + Enumerating, + Iterating, + Destructuring, + Dependent, + }; + +private: + ExpansionStmtKind PatternKind; + SourceLocation LParenLoc; + SourceLocation ColonLoc; + SourceLocation RParenLoc; + CXXExpansionStmtDecl *ParentDecl; + + /// Substatements of an unexpanded expansion statement. + /// + /// 'INIT', 'VAR', and 'BODY' are common to all kinds of expansion statements; + /// the former may be null if there is no init-statement. + /// + /// Depending on the kind of expansion statement, we may have to store + /// additional sub-statements, the first of which is denoted by + /// 'FIRST_CHILD_STATEMENT'. + /// + /// All of the sub-statements are allocated as 'TrailingObjects' so we can + /// return a single contiguous range from 'children()'. + enum SubStmt { + INIT, + VAR, + BODY, + FIRST_CHILD_STMT, + + // Enumerating expansion statement (no additional sub-statements). + COUNT_Enumerating = FIRST_CHILD_STMT, + + // Dependent expansion statement (1 additional sub-statement). + EXPANSION_INITIALIZER = FIRST_CHILD_STMT, + COUNT_Dependent, + + // Destructuring expansion statement (1 additional sub-statement). + DECOMP_DECL = FIRST_CHILD_STMT, + COUNT_Destructuring, + + // Iterating expansion statement (3 additional sub-statements). + RANGE = FIRST_CHILD_STMT, + BEGIN, + ITER, + COUNT_Iterating, + }; + + CXXExpansionStmtPattern(ExpansionStmtKind PatternKind, EmptyShell Empty); + CXXExpansionStmtPattern(ExpansionStmtKind PatternKind, + CXXExpansionStmtDecl *ESD, Stmt *Init, + DeclStmt *ExpansionVar, SourceLocation LParenLoc, + SourceLocation ColonLoc, SourceLocation RParenLoc); + +public: + static CXXExpansionStmtPattern * + CreateEmpty(ASTContext &Context, EmptyShell Empty, ExpansionStmtKind Kind); + + /// Create a dependent expansion statement pattern. + static CXXExpansionStmtPattern * + CreateDependent(ASTContext &Context, CXXExpansionStmtDecl *ESD, Stmt *Init, + DeclStmt *ExpansionVar, Expr *ExpansionInitializer, + SourceLocation LParenLoc, SourceLocation ColonLoc, + SourceLocation RParenLoc); + + /// Create a destructuring expansion statement pattern. + static CXXExpansionStmtPattern * + CreateDestructuring(ASTContext &Context, CXXExpansionStmtDecl *ESD, + Stmt *Init, DeclStmt *ExpansionVar, + Stmt *DecompositionDeclStmt, SourceLocation LParenLoc, + SourceLocation ColonLoc, SourceLocation RParenLoc); + + /// Create an enumerating expansion statement pattern. + static CXXExpansionStmtPattern * + CreateEnumerating(ASTContext &Context, CXXExpansionStmtDecl *ESD, Stmt *Init, + DeclStmt *ExpansionVar, SourceLocation LParenLoc, + SourceLocation ColonLoc, SourceLocation RParenLoc); + + /// Create an iterating expansion statement pattern. + static CXXExpansionStmtPattern * + CreateIterating(ASTContext &Context, CXXExpansionStmtDecl *ESD, Stmt *Init, + DeclStmt *ExpansionVar, DeclStmt *Range, DeclStmt *Begin, + DeclStmt* Iter, SourceLocation LParenLoc, + SourceLocation ColonLoc, SourceLocation RParenLoc); + + 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; + } + + ExpansionStmtKind getKind() const { return PatternKind; } + bool isDependent() const { + return PatternKind == ExpansionStmtKind::Dependent; + } + bool isEnumerating() const { + return PatternKind == ExpansionStmtKind::Enumerating; + } + bool isIterating() const { + return PatternKind == ExpansionStmtKind::Iterating; + } + bool isDestructuring() const { + return PatternKind == ExpansionStmtKind::Destructuring; + } + + unsigned getNumSubStmts() const { return getNumSubStmts(PatternKind); } + + // Accessors for subcomponents common to all expansion statements. + CXXExpansionStmtDecl *getDecl() { return ParentDecl; } + const CXXExpansionStmtDecl *getDecl() const { return ParentDecl; } + + Stmt *getInit() { return getSubStmt(INIT); } + const Stmt *getInit() const { return getSubStmt(INIT); } + void setInit(Stmt *S) { getSubStmt(INIT) = S; } + + VarDecl *getExpansionVariable(); + const VarDecl *getExpansionVariable() const { + return const_cast<CXXExpansionStmtPattern *>(this)->getExpansionVariable(); + } + + DeclStmt *getExpansionVarStmt() { return cast<DeclStmt>(getSubStmt(VAR)); } + const DeclStmt *getExpansionVarStmt() const { + return cast<DeclStmt>(getSubStmt(VAR)); + } + + void setExpansionVarStmt(Stmt *S) { getSubStmt(VAR) = S; } + + Stmt *getBody() { return getSubStmt(BODY); } + const Stmt *getBody() const { return getSubStmt(BODY); } + void setBody(Stmt *S) { getSubStmt(BODY) = S; } + + // Accessors for iterating statements. + const DeclStmt *getRangeVarStmt() const { + assert(isIterating()); + return cast<DeclStmt>(getSubStmt(RANGE)); + } + + DeclStmt *getRangeVarStmt() { + assert(isIterating()); + return cast<DeclStmt>(getSubStmt(RANGE)); + } + + void setRangeVarStmt(DeclStmt *S) { + assert(isIterating()); + getSubStmt(RANGE) = S; + } + + const VarDecl *getRangeVar() const { + assert(isIterating()); + return cast<VarDecl>(getRangeVarStmt()->getSingleDecl()); + } + + VarDecl *getRangeVar() { + assert(isIterating()); + return cast<VarDecl>(getRangeVarStmt()->getSingleDecl()); + } + + const DeclStmt *getBeginVarStmt() const { + assert(isIterating()); + return cast<DeclStmt>(getSubStmt(BEGIN)); + } + + DeclStmt *getBeginVarStmt() { + assert(isIterating()); + return cast<DeclStmt>(getSubStmt(BEGIN)); + } + + void setBeginVarStmt(DeclStmt *S) { + assert(isIterating()); + getSubStmt(BEGIN) = S; + } + + const VarDecl *getBeginVar() const { + assert(isIterating()); + return cast<VarDecl>(getBeginVarStmt()->getSingleDecl()); + } + + VarDecl *getBeginVar() { + assert(isIterating()); + return cast<VarDecl>(getBeginVarStmt()->getSingleDecl()); + } + + const DeclStmt *getIterVarStmt() const { + assert(isIterating()); + return cast<DeclStmt>(getSubStmt(ITER)); + } + + DeclStmt *getIterVarStmt() { + assert(isIterating()); + return cast<DeclStmt>(getSubStmt(ITER)); + } + + void setIterVarStmt(DeclStmt *S) { + assert(isIterating()); + getSubStmt(ITER) = S; + } + + const VarDecl *getIterVar() const { + assert(isIterating()); + return cast<VarDecl>(getIterVarStmt()->getSingleDecl()); + } + + VarDecl *getIterVar() { + assert(isIterating()); + return cast<VarDecl>(getIterVarStmt()->getSingleDecl()); + } + + + // Accessors for destructuring statements. + Stmt *getDecompositionDeclStmt() { + assert(isDestructuring()); + return getSubStmt(DECOMP_DECL); + } + + const Stmt *getDecompositionDeclStmt() const { + assert(isDestructuring()); + return getSubStmt(DECOMP_DECL); + } + + void setDecompositionDeclStmt(Stmt *S) { + assert(isDestructuring()); + getSubStmt(DECOMP_DECL) = S; + } + + DecompositionDecl *getDecompositionDecl(); + const DecompositionDecl *getDecompositionDecl() const { + return const_cast<CXXExpansionStmtPattern *>(this)->getDecompositionDecl(); + } + + // Accessors for dependent statements. + Expr *getExpansionInitializer() { + assert(isDependent()); + return cast<Expr>(getSubStmt(EXPANSION_INITIALIZER)); + } + + const Expr *getExpansionInitializer() const { + assert(isDependent()); + return cast<Expr>(getSubStmt(EXPANSION_INITIALIZER)); + } + + void setExpansionInitializer(Expr *S) { + assert(isDependent()); + getSubStmt(EXPANSION_INITIALIZER) = S; + } + + child_range children() { + return child_range(getTrailingObjects(), + getTrailingObjects() + getNumSubStmts()); + } + + const_child_range children() const { + return const_child_range(getTrailingObjects(), + getTrailingObjects() + getNumSubStmts()); + } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == CXXExpansionStmtPatternClass; + } + +private: + template <typename... Args> + static CXXExpansionStmtPattern *AllocateAndConstruct(ASTContext &Context, + ExpansionStmtKind Kind, + Args &&...Arguments); + + static unsigned getNumSubStmts(ExpansionStmtKind Kind); + Stmt *getSubStmt(unsigned Idx) const { + assert(Idx < getNumSubStmts()); + return getTrailingObjects()[Idx]; + } + + Stmt *&getSubStmt(unsigned Idx) { + assert(Idx < getNumSubStmts()); + return getTrailingObjects()[Idx]; + } +}; + +/// Represents the code generated for an expanded expansion statement. +/// +/// This holds 'shared statements' and 'instantiations'; these encode the +/// general underlying pattern that all expansion statements desugar to. Note +/// that only the inner '{}' (i.e. those marked as 'Actual "CompoundStmt"' +/// below) are actually present as 'CompoundStmt's in the AST; the outer braces +/// that wrap everything do *not* correspond to an actual 'CompoundStmt' and are +/// implicit in the sense that we simply push a scope when evaluating or +/// emitting IR for a 'CXXExpansionStmtInstantiation'. +/// +/// The 'instantiations' are precisely these inner compound statements. +/// +/// \verbatim +/// { // Not actually present in the AST. +/// <shared statements> +/// { // Actual 'CompoundStmt'. +/// <1st instantiation> +/// } +/// ... +/// { // Actual 'CompoundStmt'. +/// <n-th instantiation> +/// } +/// } +/// \endverbatim +/// +/// For example, the CXXExpansionStmtInstantiation that corresponds to the +/// following expansion statement +/// +/// \verbatim +/// std::tuple<int, int, int> a{1, 2, 3}; +/// template for (auto x : a) { +/// // ... +/// } +/// \endverbatim +/// +/// would be +/// +/// \verbatim +/// { +/// auto [__u0, __u1, __u2] = a; +/// { +/// auto x = __u0; +/// // ... +/// } +/// { +/// auto x = __u1; +/// // ... +/// } +/// { +/// auto x = __u2; +/// // ... +/// } +/// } +/// \endverbatim +/// +/// There are two reasons why this needs to exist and why we don't just store a +/// list of instantiations in some other node: +/// +/// 1. We need custom codegen to handle break/continue in expansion statements +/// properly, so it can't just be a compound statement. +/// +/// 2. The expansions are created after both the pattern and the +/// 'CXXExpansionStmtDecl', so we can't just store them as trailing data in +/// either of those nodes (because we don't know how many expansions there +/// will be when those notes are allocated). +/// +/// \see CXXExpansionStmtDecl +class CXXExpansionStmtInstantiation final + : public Stmt, + llvm::TrailingObjects<CXXExpansionStmtInstantiation, Stmt *> { + friend class ASTStmtReader; + friend TrailingObjects; + + SourceLocation BeginLoc; + SourceLocation EndLoc; + + // Instantiations are stored first, then shared statements. + const unsigned NumInstantiations : 20; + const unsigned NumSharedStmts : 3; + unsigned ShouldApplyLifetimeExtensionToSharedStmts : 1; + + CXXExpansionStmtInstantiation(EmptyShell Empty, unsigned NumInstantiations, + unsigned NumSharedStmts); + CXXExpansionStmtInstantiation(SourceLocation BeginLoc, SourceLocation EndLoc, + ArrayRef<Stmt *> Instantiations, + ArrayRef<Stmt *> SharedStmts, + bool ShouldApplyLifetimeExtensionToSharedStmts); + +public: + static CXXExpansionStmtInstantiation * + Create(ASTContext &C, SourceLocation BeginLoc, SourceLocation EndLoc, + ArrayRef<Stmt *> Instantiations, ArrayRef<Stmt *> SharedStmts, + bool ShouldApplyLifetimeExtensionToSharedStmts); + + static CXXExpansionStmtInstantiation *CreateEmpty(ASTContext &C, + EmptyShell Empty, + unsigned NumInstantiations, + unsigned NumSharedStmts); + + ArrayRef<Stmt *> getAllSubStmts() const { + return getTrailingObjects(getNumSubStmts()); + } + + MutableArrayRef<Stmt *> getAllSubStmts() { + return getTrailingObjects(getNumSubStmts()); + } + + unsigned getNumSubStmts() const { return NumInstantiations + NumSharedStmts; } + + ArrayRef<Stmt *> getInstantiations() const { + return getTrailingObjects(NumInstantiations); + } + + ArrayRef<Stmt *> getSharedStmts() const { + return getAllSubStmts().drop_front(NumInstantiations); + } + + bool shouldApplyLifetimeExtensionToSharedStmts() const { + return ShouldApplyLifetimeExtensionToSharedStmts; + } + + void setShouldApplyLifetimeExtensionToSharedStmts(bool Apply) { + ShouldApplyLifetimeExtensionToSharedStmts = Apply; + } + + SourceLocation getBeginLoc() const { return BeginLoc; } + SourceLocation getEndLoc() const { return EndLoc; } + + child_range children() { + Stmt **S = getTrailingObjects(); + return child_range(S, S + getNumSubStmts()); + } + + const_child_range children() const { + Stmt *const *S = getTrailingObjects(); + return const_child_range(S, S + getNumSubStmts()); + } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == CXXExpansionStmtInstantiationClass; + } +}; + } // end namespace clang #endif diff --git a/clang/include/clang/AST/TextNodeDumper.h b/clang/include/clang/AST/TextNodeDumper.h index 32e83ebb5c8eb..a3e1eb4140c34 100644 --- a/clang/include/clang/AST/TextNodeDumper.h +++ b/clang/include/clang/AST/TextNodeDumper.h @@ -266,6 +266,9 @@ class TextNodeDumper void VisitCoawaitExpr(const CoawaitExpr *Node); void VisitCoreturnStmt(const CoreturnStmt *Node); void VisitCompoundStmt(const CompoundStmt *Node); + void VisitCXXExpansionStmtPattern(const CXXExpansionStmtPattern *Node); + void + VisitCXXExpansionStmtInstantiation(const CXXExpansionStmtInstantiation *Node); void VisitConstantExpr(const ConstantExpr *Node); void VisitCallExpr(const CallExpr *Node); void VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *Node); diff --git a/clang/include/clang/Basic/DeclNodes.td b/clang/include/clang/Basic/DeclNodes.td index 04311055bb600..23f8e47939bdb 100644 --- a/clang/include/clang/Basic/DeclNodes.td +++ b/clang/include/clang/Basic/DeclNodes.td @@ -101,6 +101,7 @@ def AccessSpec : DeclNode<Decl>; def Friend : DeclNode<Decl>; def FriendTemplate : DeclNode<Decl>; def StaticAssert : DeclNode<Decl>; +def CXXExpansionStmt : DeclNode<Decl>, DeclContext; def Block : DeclNode<Decl, "blocks">, DeclContext; def OutlinedFunction : DeclNode<Decl>, DeclContext; def Captured : DeclNode<Decl>, DeclContext; diff --git a/clang/include/clang/Basic/StmtNodes.td b/clang/include/clang/Basic/StmtNodes.td index 61d76bafdfcde..8f0d875b58d99 100644 --- a/clang/include/clang/Basic/StmtNodes.td +++ b/clang/include/clang/Basic/StmtNodes.td @@ -60,6 +60,11 @@ def CXXForRangeStmt : StmtNode<Stmt>; def CoroutineBodyStmt : StmtNode<Stmt>; def CoreturnStmt : StmtNode<Stmt>; +// C++ expansion statements (P1306) +def CXXExpansionStmtPattern : StmtNode<Stmt>; +def CXXExpansionStmtInstantiation + : StmtNode<Stmt>; // *Not* derived from CXXExpansionStmtPattern! + // Expressions def Expr : StmtNode<ValueStmt, 1>; def PredefinedExpr : StmtNode<Expr>; @@ -184,6 +189,9 @@ def RequiresExpr : StmtNode<Expr>; // c++ 26 reflection def CXXReflectExpr : StmtNode<Expr>; +// C++26 Expansion statement support expressions +def CXXExpansionSelectExpr : StmtNode<Expr>; + // Obj-C Expressions. def ObjCObjectLiteral : StmtNode<Expr, 1>; def ObjCStringLiteral : StmtNode<ObjCObjectLiteral>; diff --git a/clang/include/clang/Sema/Scope.h b/clang/include/clang/Sema/Scope.h index 0d1c0ff6a1e91..fb429b73f8627 100644 --- a/clang/include/clang/Sema/Scope.h +++ b/clang/include/clang/Sema/Scope.h @@ -196,6 +196,16 @@ class Scope { /// declared in this scope. unsigned short PrototypeIndex; + /// IsExpansionStmtScope - This is the scope corresponding to a C++26 + /// expansion statement. + /// + /// FIXME: This should be part of ScopeFlags, but we're out of bits, so we + /// need to update every place that uses 'unsigned' to hold scope flags. We + /// should probably redefine ScopeFlags as an 'enum class : uint64_t' and + /// use LLVM_MARK_AS_BITMASK_ENUM() and friends so we can continue to '|' + /// scope flags together. + bool IsExpansionStmtScope; + /// FnParent - If this scope has a parent scope that is a function body, this /// pointer is non-null and points to it. This is used for label processing. Scope *FnParent; @@ -317,6 +327,14 @@ class Scope { return Flags & ConditionVarScope; } + void setIsExpansionStmtScope(bool Value = true) { + IsExpansionStmtScope = Value; + } + + bool isExpansionStmtScope() const { + return IsExpansionStmtScope; + } + /// getBreakParent - Return the closest scope that a break statement /// would be affected by. Scope *getBreakParent() { diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index 2c394fd03e8ef..6589da6a04549 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -1466,6 +1466,9 @@ enum DeclCode { /// \brief A StaticAssertDecl record. DECL_STATIC_ASSERT, + /// A C++ expansion statement. + DECL_EXPANSION_STMT, + /// A record containing CXXBaseSpecifiers. DECL_CXX_BASE_SPECIFIERS, @@ -1845,6 +1848,12 @@ enum StmtCode { STMT_CXX_FOR_RANGE, + /// A CXXExpansionPatternStmt. + STMT_CXX_EXPANSION_PATTERN, + + /// A CXXExpansionInstantiationStmt. + STMT_CXX_EXPANSION_INSTANTIATION, + /// A CXXOperatorCallExpr record. EXPR_CXX_OPERATOR_CALL, @@ -1936,6 +1945,7 @@ enum StmtCode { EXPR_CXX_FOLD, // CXXFoldExpr EXPR_CONCEPT_SPECIALIZATION, // ConceptSpecializationExpr EXPR_REQUIRES, // RequiresExpr + EXPR_CXX_EXPANSION_SELECT, // CXXExpansionSelectExpr // Reflection EXPR_REFLECT, diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index 41ba98c53247d..be8acb46ef151 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -516,6 +516,7 @@ namespace clang { ExpectedDecl VisitEmptyDecl(EmptyDecl *D); ExpectedDecl VisitAccessSpecDecl(AccessSpecDecl *D); ExpectedDecl VisitStaticAssertDecl(StaticAssertDecl *D); + ExpectedDecl VisitCXXExpansionStmtDecl(CXXExpansionStmtDecl *D); ExpectedDecl VisitTranslationUnitDecl(TranslationUnitDecl *D); ExpectedDecl VisitBindingDecl(BindingDecl *D); ExpectedDecl VisitNamespaceDecl(NamespaceDecl *D); @@ -608,6 +609,9 @@ namespace clang { ExpectedStmt VisitCXXCatchStmt(CXXCatchStmt *S); ExpectedStmt VisitCXXTryStmt(CXXTryStmt *S); ExpectedStmt VisitCXXForRangeStmt(CXXForRangeStmt *S); + ExpectedStmt VisitCXXExpansionStmtPattern(CXXExpansionStmtPattern *S); + ExpectedStmt + VisitCXXExpansionStmtInstantiation(CXXExpansionStmtInstantiation *S); // FIXME: MSDependentExistsStmt ExpectedStmt VisitObjCForCollectionStmt(ObjCForCollectionStmt *S); ExpectedStmt VisitObjCAtCatchStmt(ObjCAtCatchStmt *S); @@ -700,6 +704,7 @@ namespace clang { VisitSubstNonTypeTemplateParmPackExpr(SubstNonTypeTemplateParmPackExpr *E); ExpectedStmt VisitPseudoObjectExpr(PseudoObjectExpr *E); ExpectedStmt VisitCXXParenListInitExpr(CXXParenListInitExpr *E); + ExpectedStmt VisitCXXExpansionSelectExpr(CXXExpansionSelectExpr *E); // Helper for chaining together multiple imports. If an error is detected, // subsequent imports will return default constructed nodes, so that failure @@ -2863,6 +2868,34 @@ ExpectedDecl ASTNodeImporter::VisitStaticAssertDecl(StaticAssertDecl *D) { return ToD; } +ExpectedDecl +ASTNodeImporter::VisitCXXExpansionStmtDecl(CXXExpansionStmtDecl *D) { + auto DCOrErr = Importer.ImportContext(D->getDeclContext()); + if (!DCOrErr) + return DCOrErr.takeError(); + DeclContext *DC = *DCOrErr; + DeclContext *LexicalDC = DC; + + Error Err = Error::success(); + auto ToLocation = importChecked(Err, D->getLocation()); + auto ToExpansion = importChecked(Err, D->getExpansionPattern()); + auto ToIndex = importChecked(Err, D->getIndexTemplateParm()); + auto ToInstantiations = importChecked(Err, D->getInstantiations()); + if (Err) + return std::move(Err); + + CXXExpansionStmtDecl *ToD; + if (GetImportedOrCreateDecl(ToD, D, Importer.getToContext(), DC, ToLocation, + ToIndex)) + return ToD; + + ToD->setExpansionPattern(ToExpansion); + ToD->setInstantiations(ToInstantiations); + ToD->setLexicalDeclContext(LexicalDC); + LexicalDC->addDeclInternal(ToD); + return ToD; +} + ExpectedDecl ASTNodeImporter::VisitNamespaceDecl(NamespaceDecl *D) { // Import the major distinguishing characteristics of this namespace. DeclContext *DC, *LexicalDC; @@ -7463,6 +7496,81 @@ ExpectedStmt ASTNodeImporter::VisitCXXForRangeStmt(CXXForRangeStmt *S) { ToBody, ToForLoc, ToCoawaitLoc, ToColonLoc, ToRParenLoc); } +ExpectedStmt ASTNodeImporter::VisitCXXExpansionStmtPattern( + CXXExpansionStmtPattern *S) { + Error Err = Error::success(); + auto ToESD = importChecked(Err, S->getDecl()); + auto ToInit = importChecked(Err, S->getInit()); + auto ToExpansionVar = importChecked(Err, S->getExpansionVarStmt()); + auto ToLParenLoc = importChecked(Err, S->getLParenLoc()); + auto ToColonLoc = importChecked(Err, S->getColonLoc()); + auto ToRParenLoc = importChecked(Err, S->getRParenLoc()); + if (Err) + return std::move(Err); + + switch (S->getKind()) { + case CXXExpansionStmtPattern::ExpansionStmtKind::Enumerating: + return CXXExpansionStmtPattern::CreateEnumerating( + Importer.getToContext(), ToESD, ToInit, ToExpansionVar, ToLParenLoc, + ToColonLoc, ToRParenLoc); + + case CXXExpansionStmtPattern::ExpansionStmtKind::Iterating: { + auto ToRange = importChecked(Err, S->getRangeVarStmt()); + auto ToBegin = importChecked(Err, S->getBeginVarStmt()); + auto ToIter = importChecked(Err, S->getIterVarStmt()); + if (Err) + return std::move(Err); + + return CXXExpansionStmtPattern::CreateIterating( + Importer.getToContext(), ToESD, ToInit, ToExpansionVar, ToRange, + ToBegin, ToIter, ToLParenLoc, ToColonLoc, ToRParenLoc); + } + + case CXXExpansionStmtPattern::ExpansionStmtKind::Destructuring: { + auto ToDecompositionDeclStmt = + importChecked(Err, S->getDecompositionDeclStmt()); + if (Err) + return std::move(Err); + + return CXXExpansionStmtPattern::CreateDestructuring( + Importer.getToContext(), ToESD, ToInit, ToExpansionVar, + ToDecompositionDeclStmt, ToLParenLoc, ToColonLoc, ToRParenLoc); + } + + case CXXExpansionStmtPattern::ExpansionStmtKind::Dependent: { + auto ToExpansionInitializer = + importChecked(Err, S->getExpansionInitializer()); + if (Err) + return std::move(Err); + return CXXExpansionStmtPattern::CreateDependent( + Importer.getToContext(), ToESD, ToInit, ToExpansionVar, + ToExpansionInitializer, ToLParenLoc, ToColonLoc, ToRParenLoc); + } + } + + llvm_unreachable("invalid pattern kind"); +} + +ExpectedStmt ASTNodeImporter::VisitCXXExpansionStmtInstantiation( + CXXExpansionStmtInstantiation *S) { + Error Err = Error::success(); + SmallVector<Stmt *> ToInstantiations; + SmallVector<Stmt *> ToSharedStmts; + auto ToBeginLoc = importChecked(Err, S->getBeginLoc()); + auto ToEndLoc = importChecked(Err, S->getEndLoc()); + for (Stmt *FromInst : S->getInstantiations()) + ToInstantiations.push_back(importChecked(Err, FromInst)); + for (Stmt *FromShared : S->getSharedStmts()) + ToSharedStmts.push_back(importChecked(Err, FromShared)); + + if (Err) + return std::move(Err); + + return CXXExpansionStmtInstantiation::Create( + Importer.getToContext(), ToBeginLoc, ToEndLoc, ToInstantiations, + ToSharedStmts, S->shouldApplyLifetimeExtensionToSharedStmts()); +} + ExpectedStmt ASTNodeImporter::VisitObjCForCollectionStmt(ObjCForCollectionStmt *S) { Error Err = Error::success(); @@ -9359,6 +9467,18 @@ ASTNodeImporter::VisitCXXParenListInitExpr(CXXParenListInitExpr *E) { ToInitLoc, ToBeginLoc, ToEndLoc); } +ExpectedStmt ASTNodeImporter::VisitCXXExpansionSelectExpr( + CXXExpansionSelectExpr *E) { + Error Err = Error::success(); + auto ToRange = importChecked(Err, E->getRangeExpr()); + auto ToIndex = importChecked(Err, E->getIndexExpr()); + if (Err) + return std::move(Err); + + return new (Importer.getToContext()) + CXXExpansionSelectExpr(Importer.getToContext(), ToRange, ToIndex); +} + Error ASTNodeImporter::ImportOverriddenMethods(CXXMethodDecl *ToMethod, CXXMethodDecl *FromMethod) { Error ImportErrors = Error::success(); diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp index 0a1e442656c35..e81bea6302456 100644 --- a/clang/lib/AST/DeclBase.cpp +++ b/clang/lib/AST/DeclBase.cpp @@ -325,6 +325,9 @@ unsigned Decl::getTemplateDepth() const { if (auto *TPL = getDescribedTemplateParams()) return TPL->getDepth() + 1; + if (auto *ESD = dyn_cast<CXXExpansionStmtDecl>(this)) + return ESD->getIndexTemplateParm()->getDepth() + 1; + // If this is a dependent lambda, there might be an enclosing variable // template. In this case, the next step is not the parent DeclContext (or // even a DeclContext at all). @@ -1018,6 +1021,7 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) { case ImplicitConceptSpecialization: case OpenACCDeclare: case OpenACCRoutine: + case CXXExpansionStmt: // Never looked up by name. return 0; } @@ -1382,7 +1386,7 @@ bool DeclContext::isDependentContext() const { if (isFileContext()) return false; - if (isa<ClassTemplatePartialSpecializationDecl>(this)) + if (isa<ClassTemplatePartialSpecializationDecl, CXXExpansionStmtDecl>(this)) return true; if (const auto *Record = dyn_cast<CXXRecordDecl>(this)) { @@ -1491,6 +1495,7 @@ DeclContext *DeclContext::getPrimaryContext() { case Decl::OMPDeclareReduction: case Decl::OMPDeclareMapper: case Decl::RequiresExprBody: + case Decl::CXXExpansionStmt: // There is only one DeclContext for these entities. return this; @@ -2079,6 +2084,13 @@ RecordDecl *DeclContext::getOuterLexicalRecordContext() { return OutermostRD; } +DeclContext *DeclContext::getEnclosingNonExpansionStatementContext() { + DeclContext *DC = this; + while (isa<CXXExpansionStmtDecl>(DC)) + DC = DC->getParent(); + return DC; +} + bool DeclContext::InEnclosingNamespaceSetOf(const DeclContext *O) const { // For non-file contexts, this is equivalent to Equals. if (!isFileContext()) diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp index 5e377a6c0c247..fb5631c8673b0 100644 --- a/clang/lib/AST/DeclPrinter.cpp +++ b/clang/lib/AST/DeclPrinter.cpp @@ -114,6 +114,7 @@ namespace { void VisitNonTypeTemplateParmDecl(const NonTypeTemplateParmDecl *NTTP); void VisitTemplateTemplateParmDecl(const TemplateTemplateParmDecl *); void VisitHLSLBufferDecl(HLSLBufferDecl *D); + void VisitCXXExpansionStmtDecl(const CXXExpansionStmtDecl *D); void VisitOpenACCDeclareDecl(OpenACCDeclareDecl *D); void VisitOpenACCRoutineDecl(OpenACCRoutineDecl *D); @@ -1347,6 +1348,11 @@ void DeclPrinter::VisitClassTemplatePartialSpecializationDecl( VisitCXXRecordDecl(D); } +void DeclPrinter::VisitCXXExpansionStmtDecl(const CXXExpansionStmtDecl *D) { + D->getExpansionPattern()->printPretty(Out, /*PrinterHelper=*/nullptr, Policy, + Indentation, "\n", &Context); +} + //---------------------------------------------------------------------------- // Objective-C declarations //---------------------------------------------------------------------------- diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp index 5a8e1ed445f3a..7d0a214e25813 100644 --- a/clang/lib/AST/DeclTemplate.cpp +++ b/clang/lib/AST/DeclTemplate.cpp @@ -1711,6 +1711,9 @@ clang::getReplacedTemplateParameter(Decl *D, unsigned Index) { return getReplacedTemplateParameter( cast<FunctionDecl>(D)->getTemplateSpecializationInfo()->getTemplate(), Index); + case Decl::Kind::CXXExpansionStmt: + assert(Index == 0 && "expansion stmts only have a single template param"); + return {cast<CXXExpansionStmtDecl>(D)->getIndexTemplateParm(), {}}; default: llvm_unreachable("Unhandled templated declaration kind"); } @@ -1782,3 +1785,23 @@ const Decl &clang::adjustDeclToTemplate(const Decl &D) { // FIXME: Adjust alias templates? return D; } + +CXXExpansionStmtDecl::CXXExpansionStmtDecl(DeclContext *DC, SourceLocation Loc, + NonTypeTemplateParmDecl *NTTP) + : Decl(CXXExpansionStmt, DC, Loc), DeclContext(CXXExpansionStmt), + IndexNTTP(NTTP) {} + +CXXExpansionStmtDecl * +CXXExpansionStmtDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation Loc, + NonTypeTemplateParmDecl *NTTP) { + return new (C, DC) CXXExpansionStmtDecl(DC, Loc, NTTP); +} +CXXExpansionStmtDecl * +CXXExpansionStmtDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID) { + return new (C, ID) + CXXExpansionStmtDecl(/*DC=*/nullptr, SourceLocation(), /*NTTP=*/nullptr); +} + +SourceRange CXXExpansionStmtDecl::getSourceRange() const { + return Pattern ? Pattern->getSourceRange() : SourceRange(); +} diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index 64d61dbc3d128..ba2f97b5b4b8d 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -3711,6 +3711,7 @@ bool Expr::HasSideEffects(const ASTContext &Ctx, case FunctionParmPackExprClass: case RecoveryExprClass: case CXXFoldExprClass: + case CXXExpansionSelectExprClass: // Make a conservative assumption for dependent nodes. return IncludePossibleEffects; diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp index dd603bf548926..c50ff64f9de64 100644 --- a/clang/lib/AST/ExprCXX.cpp +++ b/clang/lib/AST/ExprCXX.cpp @@ -2036,3 +2036,15 @@ CXXFoldExpr::CXXFoldExpr(QualType T, UnresolvedLookupExpr *Callee, SubExprs[SubExpr::RHS] = RHS; setDependence(computeDependence(this)); } + +CXXExpansionSelectExpr::CXXExpansionSelectExpr(EmptyShell Empty) + : Expr(CXXExpansionSelectExprClass, Empty) {} + +CXXExpansionSelectExpr::CXXExpansionSelectExpr( + const ASTContext &C, InitListExpr *Range, Expr *Idx) + : Expr(CXXExpansionSelectExprClass, C.DependentTy, VK_PRValue, + OK_Ordinary) { + setDependence(ExprDependence::TypeValueInstantiation); + SubExprs[RANGE] = Range; + SubExprs[INDEX] = Idx; +} diff --git a/clang/lib/AST/ExprClassification.cpp b/clang/lib/AST/ExprClassification.cpp index a83c17074ea69..502a681ddf2a0 100644 --- a/clang/lib/AST/ExprClassification.cpp +++ b/clang/lib/AST/ExprClassification.cpp @@ -218,6 +218,7 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { case Expr::ConceptSpecializationExprClass: case Expr::RequiresExprClass: case Expr::CXXReflectExprClass: + case Expr::CXXExpansionSelectExprClass: return Cl::CL_PRValue; case Expr::EmbedExprClass: diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 4f45fa728c605..a464ed90f70b8 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -21423,6 +21423,7 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) { case Expr::SYCLUniqueStableNameExprClass: case Expr::CXXParenListInitExprClass: case Expr::HLSLOutArgExprClass: + case Expr::CXXExpansionSelectExprClass: return ICEDiag(IK_NotICE, E->getBeginLoc()); case Expr::MemberExprClass: { diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index ccf5717073fbf..ab1353321635b 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -45,7 +45,7 @@ namespace UnsupportedItaniumManglingKind = namespace { static bool isLocalContainerContext(const DeclContext *DC) { - return isa<FunctionDecl>(DC) || isa<ObjCMethodDecl>(DC) || isa<BlockDecl>(DC); + return isa<FunctionDecl, ObjCMethodDecl, BlockDecl, CXXExpansionStmtDecl>(DC); } static const FunctionDecl *getStructor(const FunctionDecl *fn) { @@ -1876,6 +1876,8 @@ static GlobalDecl getParentOfLocalEntity(const DeclContext *DC) { GD = GlobalDecl(CD, Ctor_Complete); else if (auto *DD = dyn_cast<CXXDestructorDecl>(DC)) GD = GlobalDecl(DD, Dtor_Complete); + else if (DC->isExpansionStmt()) + GD = getParentOfLocalEntity(DC->getEnclosingNonExpansionStatementContext()); else GD = GlobalDecl(cast<FunctionDecl>(DC)); return GD; @@ -2217,6 +2219,9 @@ void CXXNameMangler::manglePrefix(const DeclContext *DC, bool NoFunction) { if (NoFunction && isLocalContainerContext(DC)) return; + if (DC->isExpansionStmt()) + return; + const NamedDecl *ND = cast<NamedDecl>(DC); if (mangleSubstitution(ND)) return; @@ -4977,6 +4982,7 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity, case Expr::CXXInheritedCtorInitExprClass: case Expr::CXXParenListInitExprClass: case Expr::PackIndexingExprClass: + case Expr::CXXExpansionSelectExprClass: llvm_unreachable("unexpected statement kind"); case Expr::ConstantExprClass: diff --git a/clang/lib/AST/StmtCXX.cpp b/clang/lib/AST/StmtCXX.cpp index 6a69fe75136f3..e7b9b8ad3e64e 100644 --- a/clang/lib/AST/StmtCXX.cpp +++ b/clang/lib/AST/StmtCXX.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "clang/AST/StmtCXX.h" +#include "clang/AST/ExprCXX.h" #include "clang/AST/ASTContext.h" @@ -125,3 +126,159 @@ CoroutineBodyStmt::CoroutineBodyStmt(CoroutineBodyStmt::CtorArgs const &Args) Args.ReturnStmtOnAllocFailure; llvm::copy(Args.ParamMoves, const_cast<Stmt **>(getParamMoves().data())); } + +CXXExpansionStmtPattern::CXXExpansionStmtPattern(ExpansionStmtKind PatternKind, + EmptyShell Empty) + : Stmt(CXXExpansionStmtPatternClass, Empty), PatternKind(PatternKind) {} + +CXXExpansionStmtPattern::CXXExpansionStmtPattern( + ExpansionStmtKind PatternKind, CXXExpansionStmtDecl *ESD, Stmt *Init, + DeclStmt *ExpansionVar, SourceLocation LParenLoc, SourceLocation ColonLoc, + SourceLocation RParenLoc) + : Stmt(CXXExpansionStmtPatternClass), PatternKind(PatternKind), + LParenLoc(LParenLoc), ColonLoc(ColonLoc), RParenLoc(RParenLoc), + ParentDecl(ESD) { + setInit(Init); + setExpansionVarStmt(ExpansionVar); + setBody(nullptr); +} + +template <typename... Args> +CXXExpansionStmtPattern *CXXExpansionStmtPattern::AllocateAndConstruct( + ASTContext &Context, ExpansionStmtKind Kind, Args &&...Arguments) { + std::size_t Size = totalSizeToAlloc<Stmt *>(getNumSubStmts(Kind)); + void *Mem = Context.Allocate(Size, alignof(CXXExpansionStmtPattern)); + return new (Mem) + CXXExpansionStmtPattern(Kind, std::forward<Args>(Arguments)...); +} + +CXXExpansionStmtPattern *CXXExpansionStmtPattern::CreateDependent( + ASTContext &Context, CXXExpansionStmtDecl *ESD, Stmt *Init, + DeclStmt *ExpansionVar, Expr *ExpansionInitializer, + SourceLocation LParenLoc, SourceLocation ColonLoc, + SourceLocation RParenLoc) { + CXXExpansionStmtPattern *Pattern = + AllocateAndConstruct(Context, ExpansionStmtKind::Dependent, ESD, Init, + ExpansionVar, LParenLoc, ColonLoc, RParenLoc); + Pattern->setExpansionInitializer(ExpansionInitializer); + return Pattern; +} + +CXXExpansionStmtPattern *CXXExpansionStmtPattern::CreateDestructuring( + ASTContext &Context, CXXExpansionStmtDecl *ESD, Stmt *Init, + DeclStmt *ExpansionVar, Stmt *DecompositionDeclStmt, + SourceLocation LParenLoc, SourceLocation ColonLoc, + SourceLocation RParenLoc) { + CXXExpansionStmtPattern *Pattern = + AllocateAndConstruct(Context, ExpansionStmtKind::Destructuring, ESD, Init, + ExpansionVar, LParenLoc, ColonLoc, RParenLoc); + Pattern->setDecompositionDeclStmt(DecompositionDeclStmt); + return Pattern; +} + +CXXExpansionStmtPattern * +CXXExpansionStmtPattern::CreateEmpty(ASTContext &Context, EmptyShell Empty, + ExpansionStmtKind Kind) { + return AllocateAndConstruct(Context, Kind, Empty); +} + +CXXExpansionStmtPattern *CXXExpansionStmtPattern::CreateEnumerating( + ASTContext &Context, CXXExpansionStmtDecl *ESD, Stmt *Init, + DeclStmt *ExpansionVar, SourceLocation LParenLoc, SourceLocation ColonLoc, + SourceLocation RParenLoc) { + return AllocateAndConstruct(Context, ExpansionStmtKind::Enumerating, ESD, + Init, ExpansionVar, LParenLoc, ColonLoc, + RParenLoc); +} + +CXXExpansionStmtPattern *CXXExpansionStmtPattern::CreateIterating( + ASTContext &Context, CXXExpansionStmtDecl *ESD, Stmt *Init, + DeclStmt *ExpansionVar, DeclStmt *Range, DeclStmt *Begin, + DeclStmt *Iter, SourceLocation LParenLoc, SourceLocation ColonLoc, + SourceLocation RParenLoc) { + CXXExpansionStmtPattern *Pattern = + AllocateAndConstruct(Context, ExpansionStmtKind::Iterating, ESD, Init, + ExpansionVar, LParenLoc, ColonLoc, RParenLoc); + Pattern->setRangeVarStmt(Range); + Pattern->setBeginVarStmt(Begin); + Pattern->setIterVarStmt(Iter); + return Pattern; +} + +SourceLocation CXXExpansionStmtPattern::getBeginLoc() const { + return ParentDecl->getLocation(); +} + +DecompositionDecl * +CXXExpansionStmtPattern::getDecompositionDecl() { + assert(isDestructuring()); + return cast<DecompositionDecl>( + cast<DeclStmt>(getDecompositionDeclStmt())->getSingleDecl()); +} + +VarDecl *CXXExpansionStmtPattern::getExpansionVariable() { + Decl *LV = cast<DeclStmt>(getExpansionVarStmt())->getSingleDecl(); + assert(LV && "No expansion variable in CXXExpansionStmtPattern"); + return cast<VarDecl>(LV); +} + +unsigned +CXXExpansionStmtPattern::getNumSubStmts(ExpansionStmtKind PatternKind) { + switch (PatternKind) { + case ExpansionStmtKind::Enumerating: + return COUNT_Enumerating; + case ExpansionStmtKind::Iterating: + return COUNT_Iterating; + case ExpansionStmtKind::Destructuring: + return COUNT_Destructuring; + case ExpansionStmtKind::Dependent: + return COUNT_Dependent; + } + + llvm_unreachable("invalid pattern kind"); +} + +CXXExpansionStmtInstantiation::CXXExpansionStmtInstantiation( + EmptyShell Empty, unsigned NumInstantiations, unsigned NumSharedStmts) + : Stmt(CXXExpansionStmtInstantiationClass, Empty), + NumInstantiations(NumInstantiations), NumSharedStmts(NumSharedStmts) { + assert(NumSharedStmts <= 4 && "might have to allocate more bits for this"); +} + +CXXExpansionStmtInstantiation::CXXExpansionStmtInstantiation( + SourceLocation BeginLoc, SourceLocation EndLoc, + ArrayRef<Stmt *> Instantiations, ArrayRef<Stmt *> SharedStmts, + bool ShouldApplyLifetimeExtensionToSharedStmts) + : Stmt(CXXExpansionStmtInstantiationClass), BeginLoc(BeginLoc), + EndLoc(EndLoc), NumInstantiations(unsigned(Instantiations.size())), + NumSharedStmts(unsigned(SharedStmts.size())), + ShouldApplyLifetimeExtensionToSharedStmts( + ShouldApplyLifetimeExtensionToSharedStmts) { + assert(NumSharedStmts <= 4 && "might have to allocate more bits for this"); + llvm::uninitialized_copy(Instantiations, getTrailingObjects()); + llvm::uninitialized_copy(SharedStmts, + getTrailingObjects() + NumInstantiations); +} + +CXXExpansionStmtInstantiation *CXXExpansionStmtInstantiation::Create( + ASTContext &C, SourceLocation BeginLoc, SourceLocation EndLoc, + ArrayRef<Stmt *> Instantiations, ArrayRef<Stmt *> SharedStmts, + bool ShouldApplyLifetimeExtensionToSharedStmts) { + void *Mem = C.Allocate( + totalSizeToAlloc<Stmt *>(Instantiations.size() + SharedStmts.size()), + alignof(CXXExpansionStmtInstantiation)); + return new (Mem) CXXExpansionStmtInstantiation( + BeginLoc, EndLoc, Instantiations, SharedStmts, + ShouldApplyLifetimeExtensionToSharedStmts); +} + +CXXExpansionStmtInstantiation * +CXXExpansionStmtInstantiation::CreateEmpty(ASTContext &C, EmptyShell Empty, + unsigned NumInstantiations, + unsigned NumSharedStmts) { + void *Mem = + C.Allocate(totalSizeToAlloc<Stmt *>(NumInstantiations + NumSharedStmts), + alignof(CXXExpansionStmtInstantiation)); + return new (Mem) + CXXExpansionStmtInstantiation(Empty, NumInstantiations, NumSharedStmts); +} diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index 4d364fdcd5502..1cc7a20c0e4bf 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -263,7 +263,8 @@ void StmtPrinter::VisitDeclStmt(DeclStmt *Node) { PrintRawDeclStmt(Node); // Certain pragma declarations shouldn't have a semi-colon after them. if (!Node->isSingleDecl() || - !isa<OpenACCDeclareDecl, OpenACCRoutineDecl>(Node->getSingleDecl())) + !isa<CXXExpansionStmtDecl, OpenACCDeclareDecl, OpenACCRoutineDecl>( + Node->getSingleDecl())) OS << ";"; OS << NL; } @@ -447,6 +448,38 @@ void StmtPrinter::VisitCXXForRangeStmt(CXXForRangeStmt *Node) { PrintControlledStmt(Node->getBody()); } +void StmtPrinter::VisitCXXExpansionStmtPattern(CXXExpansionStmtPattern *Node) { + OS << "template for ("; + if (Node->getInit()) + PrintInitStmt(Node->getInit(), 14); + PrintingPolicy SubPolicy(Policy); + SubPolicy.SuppressInitializers = true; + Node->getExpansionVariable()->print(OS, SubPolicy, IndentLevel); + OS << " : "; + + if (Node->isIterating()) + PrintExpr(Node->getRangeVar()->getInit()); + else if (Node->isDependent()) + PrintExpr(Node->getExpansionInitializer()); + else if (Node->isDestructuring()) + PrintExpr(Node->getDecompositionDecl()->getInit()); + else + PrintExpr(Node->getExpansionVariable()->getInit()); + + OS << ")"; + PrintControlledStmt(Node->getBody()); +} + +void StmtPrinter::VisitCXXExpansionStmtInstantiation( + CXXExpansionStmtInstantiation *) { + llvm_unreachable("should never be printed"); +} + +void StmtPrinter::VisitCXXExpansionSelectExpr( + CXXExpansionSelectExpr *Node) { + PrintExpr(Node->getRangeExpr()); +} + void StmtPrinter::VisitMSDependentExistsStmt(MSDependentExistsStmt *Node) { Indent(); if (Node->isIfExists()) diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp index e8c1f8a8ecb5f..43cff54c23f89 100644 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -366,6 +366,17 @@ void StmtProfiler::VisitCXXForRangeStmt(const CXXForRangeStmt *S) { VisitStmt(S); } +void StmtProfiler::VisitCXXExpansionStmtPattern( + const CXXExpansionStmtPattern *S) { + VisitStmt(S); +} + +void StmtProfiler::VisitCXXExpansionStmtInstantiation( + const CXXExpansionStmtInstantiation *S) { + VisitStmt(S); + ID.AddBoolean(S->shouldApplyLifetimeExtensionToSharedStmts()); +} + void StmtProfiler::VisitMSDependentExistsStmt(const MSDependentExistsStmt *S) { VisitStmt(S); ID.AddBoolean(S->isIfExists()); @@ -2428,6 +2439,11 @@ void StmtProfiler::VisitSourceLocExpr(const SourceLocExpr *E) { void StmtProfiler::VisitEmbedExpr(const EmbedExpr *E) { VisitExpr(E); } +void StmtProfiler::VisitCXXExpansionSelectExpr( + const CXXExpansionSelectExpr *E) { + VisitExpr(E); +} + void StmtProfiler::VisitRecoveryExpr(const RecoveryExpr *E) { VisitExpr(E); } void StmtProfiler::VisitObjCObjectLiteral(const ObjCObjectLiteral *E) { diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp index 250ec8b666e05..60df46685828a 100644 --- a/clang/lib/AST/TextNodeDumper.cpp +++ b/clang/lib/AST/TextNodeDumper.cpp @@ -968,7 +968,11 @@ void TextNodeDumper::dumpBareDeclRef(const Decl *D) { switch (ND->getKind()) { case Decl::Decomposition: { auto *DD = cast<DecompositionDecl>(ND); - OS << " first_binding '" << DD->bindings()[0]->getDeclName() << '\''; + + // Empty decomposition decls can occur in destructuring expansion + // statements. + if (!DD->bindings().empty()) + OS << " first_binding '" << DD->bindings()[0]->getDeclName() << '\''; break; } case Decl::Field: { @@ -1512,6 +1516,32 @@ void clang::TextNodeDumper::VisitCoreturnStmt(const CoreturnStmt *Node) { OS << " implicit"; } +void TextNodeDumper::VisitCXXExpansionStmtPattern( + const CXXExpansionStmtPattern *Node) { + switch (Node->getKind()) { + case CXXExpansionStmtPattern::ExpansionStmtKind::Enumerating: + OS << " enumerating"; + return; + case CXXExpansionStmtPattern::ExpansionStmtKind::Iterating: + OS << " iterating"; + return; + case CXXExpansionStmtPattern::ExpansionStmtKind::Destructuring: + OS << " destructuring"; + return; + case CXXExpansionStmtPattern::ExpansionStmtKind::Dependent: + OS << " dependent"; + return; + } + + llvm_unreachable("invalid expansion statement kind"); +} + +void TextNodeDumper::VisitCXXExpansionStmtInstantiation( + const CXXExpansionStmtInstantiation *Node) { + if (Node->shouldApplyLifetimeExtensionToSharedStmts()) + OS << " applies_lifetime_extension"; +} + void TextNodeDumper::VisitConstantExpr(const ConstantExpr *Node) { if (Node->hasAPValueResult()) AddChild("value", diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp index 8b5ffde1b73f3..047aa55dc4b4a 100644 --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -143,6 +143,9 @@ void CodeGenFunction::EmitDecl(const Decl &D, bool EvaluateConditionDecl) { // None of these decls require codegen support. return; + case Decl::CXXExpansionStmt: + llvm_unreachable("TODO"); + case Decl::NamespaceAlias: if (CGDebugInfo *DI = getDebugInfo()) DI->EmitNamespaceAlias(cast<NamespaceAliasDecl>(D)); diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp index a923002bec9b6..8d9eb67cc2cef 100644 --- a/clang/lib/CodeGen/CGStmt.cpp +++ b/clang/lib/CodeGen/CGStmt.cpp @@ -203,6 +203,10 @@ void CodeGenFunction::EmitStmt(const Stmt *S, ArrayRef<const Attr *> Attrs) { case Stmt::CXXForRangeStmtClass: EmitCXXForRangeStmt(cast<CXXForRangeStmt>(*S), Attrs); break; + case Stmt::CXXExpansionStmtPatternClass: + llvm_unreachable("unexpanded expansion statements should not be emitted"); + case Stmt::CXXExpansionStmtInstantiationClass: + llvm_unreachable("Todo"); case Stmt::SEHTryStmtClass: EmitSEHTryStmt(cast<SEHTryStmt>(*S)); break; diff --git a/clang/lib/Sema/IdentifierResolver.cpp b/clang/lib/Sema/IdentifierResolver.cpp index 2213c3c837243..5daa42e4bc9bd 100644 --- a/clang/lib/Sema/IdentifierResolver.cpp +++ b/clang/lib/Sema/IdentifierResolver.cpp @@ -99,6 +99,8 @@ IdentifierResolver::~IdentifierResolver() { /// isDeclInScope - If 'Ctx' is a function/method, isDeclInScope returns true /// if 'D' is in Scope 'S', otherwise 'S' is ignored and isDeclInScope returns /// true if 'D' belongs to the given declaration context. +/// +/// If 'Ctx' is an expansion statement, we use its enclosing function instead. bool IdentifierResolver::isDeclInScope(Decl *D, DeclContext *Ctx, Scope *S, bool AllowInlineNamespace) const { Ctx = Ctx->getRedeclContext(); @@ -107,7 +109,10 @@ bool IdentifierResolver::isDeclInScope(Decl *D, DeclContext *Ctx, Scope *S, // conflict with other Decls. if (LangOpt.HLSL && isa<HLSLBufferDecl>(D)) return false; - if (Ctx->isFunctionOrMethod() || (S && S->isFunctionPrototypeScope())) { + if (Ctx->getEnclosingNonExpansionStatementContext() + ->getRedeclContext() + ->isFunctionOrMethod() || + (S && S->isFunctionPrototypeScope())) { // Ignore the scopes associated within transparent declaration contexts. while (S->getEntity() && (S->getEntity()->isTransparentContext() || diff --git a/clang/lib/Sema/Scope.cpp b/clang/lib/Sema/Scope.cpp index e66cce255230b..997378f3d8368 100644 --- a/clang/lib/Sema/Scope.cpp +++ b/clang/lib/Sema/Scope.cpp @@ -18,6 +18,7 @@ using namespace clang; void Scope::setFlags(Scope *parent, unsigned flags) { + IsExpansionStmtScope = false; AnyParent = parent; Flags = flags; diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index ee750284e4266..50bc923ebc0a4 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -1701,14 +1701,15 @@ DeclContext *Sema::getFunctionLevelDeclContext(bool AllowLambda) const { DeclContext *DC = CurContext; while (true) { - if (isa<BlockDecl>(DC) || isa<EnumDecl>(DC) || isa<CapturedDecl>(DC) || - isa<RequiresExprBodyDecl>(DC)) { + if (isa<BlockDecl, EnumDecl, CapturedDecl, RequiresExprBodyDecl, + CXXExpansionStmtDecl>(DC)) { DC = DC->getParent(); } else if (!AllowLambda && isa<CXXMethodDecl>(DC) && cast<CXXMethodDecl>(DC)->getOverloadedOperator() == OO_Call && cast<CXXRecordDecl>(DC->getParent())->isLambda()) { DC = DC->getParent()->getParent(); - } else break; + } else + break; } return DC; diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 90307423a20b6..16a34475aac1a 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -7467,7 +7467,7 @@ static bool shouldConsiderLinkage(const VarDecl *VD) { if (DC->getDeclKind() == Decl::HLSLBuffer) return false; - if (isa<RequiresExprBodyDecl>(DC)) + if (isa<RequiresExprBodyDecl, CXXExpansionStmtDecl>(DC)) return false; llvm_unreachable("Unexpected context"); } @@ -7477,7 +7477,7 @@ static bool shouldConsiderLinkage(const FunctionDecl *FD) { if (DC->isFileContext() || DC->isFunctionOrMethod() || isa<OMPDeclareReductionDecl>(DC) || isa<OMPDeclareMapperDecl>(DC)) return true; - if (DC->isRecord()) + if (DC->isRecord() || isa<CXXExpansionStmtDecl>(DC)) return false; llvm_unreachable("Unexpected context"); } diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp index 56079ea8e1bf8..cabcb3ae7bc6d 100644 --- a/clang/lib/Sema/SemaExceptionSpec.cpp +++ b/clang/lib/Sema/SemaExceptionSpec.cpp @@ -1302,6 +1302,7 @@ CanThrowResult Sema::canThrow(const Stmt *S) { case Expr::ConvertVectorExprClass: case Expr::VAArgExprClass: case Expr::CXXParenListInitExprClass: + case Expr::CXXExpansionSelectExprClass: return canSubStmtsThrow(*this, S); case Expr::CompoundLiteralExprClass: @@ -1554,6 +1555,13 @@ CanThrowResult Sema::canThrow(const Stmt *S) { case Stmt::SwitchStmtClass: case Stmt::WhileStmtClass: case Stmt::DeferStmtClass: + case Stmt::CXXExpansionStmtInstantiationClass: + return canSubStmtsThrow(*this, S); + + case Stmt::CXXExpansionStmtPatternClass: + if (auto *Pattern = cast<CXXExpansionStmtPattern>(S); + Pattern->isDependent()) + return CT_Dependent; return canSubStmtsThrow(*this, S); case Stmt::DeclStmtClass: { diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 102b6315b4e3b..66306d172a462 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -19679,11 +19679,12 @@ bool Sema::tryCaptureVariable( QualType &DeclRefType, const unsigned *const FunctionScopeIndexToStopAt) { // An init-capture is notionally from the context surrounding its // declaration, but its parent DC is the lambda class. - DeclContext *VarDC = Var->getDeclContext(); + DeclContext *VarDC = + Var->getDeclContext()->getEnclosingNonExpansionStatementContext(); DeclContext *DC = CurContext; // Skip past RequiresExprBodys because they don't constitute function scopes. - while (DC->isRequiresExprBody()) + while (DC->isRequiresExprBody() || DC->isExpansionStmt()) DC = DC->getParent(); // tryCaptureVariable is called every time a DeclRef is formed, diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index f7e005a40363c..b47fbbf691133 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -7645,7 +7645,6 @@ static void CheckIfAnyEnclosingLambdasMustCaptureAnyPotentialCaptures( Expr *const FE, LambdaScopeInfo *const CurrentLSI, Sema &S) { assert(!S.isUnevaluatedContext()); - assert(S.CurContext->isDependentContext()); #ifndef NDEBUG DeclContext *DC = S.CurContext; while (isa_and_nonnull<CapturedDecl>(DC)) diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp index 8572e3a742a6c..db3eae4be1c41 100644 --- a/clang/lib/Sema/SemaLambda.cpp +++ b/clang/lib/Sema/SemaLambda.cpp @@ -100,8 +100,9 @@ static inline UnsignedOrNone getStackIndexOfNearestEnclosingCaptureReadyLambda( // innermost nested lambda are dependent (otherwise we wouldn't have // arrived here) - so we don't yet have a lambda that can capture the // variable. - if (IsCapturingVariable && - VarToCapture->getDeclContext()->Equals(EnclosingDC)) + if (IsCapturingVariable && VarToCapture->getDeclContext() + ->getEnclosingNonExpansionStatementContext() + ->Equals(EnclosingDC)) return NoLambdaIsCaptureReady; // For an enclosing lambda to be capture ready for an entity, all @@ -126,7 +127,8 @@ static inline UnsignedOrNone getStackIndexOfNearestEnclosingCaptureReadyLambda( if (IsCapturingThis && !LSI->isCXXThisCaptured()) return NoLambdaIsCaptureReady; } - EnclosingDC = getLambdaAwareParentOfDeclContext(EnclosingDC); + EnclosingDC = getLambdaAwareParentOfDeclContext(EnclosingDC) + ->getEnclosingNonExpansionStatementContext(); assert(CurScopeIndex); --CurScopeIndex; @@ -190,11 +192,6 @@ UnsignedOrNone clang::getStackIndexOfNearestEnclosingCaptureCapableLambda( return NoLambdaIsCaptureCapable; const unsigned IndexOfCaptureReadyLambda = *OptionalStackIndex; - assert(((IndexOfCaptureReadyLambda != (FunctionScopes.size() - 1)) || - S.getCurGenericLambda()) && - "The capture ready lambda for a potential capture can only be the " - "current lambda if it is a generic lambda"); - const sema::LambdaScopeInfo *const CaptureReadyLambdaLSI = cast<sema::LambdaScopeInfo>(FunctionScopes[IndexOfCaptureReadyLambda]); @@ -248,7 +245,7 @@ CXXRecordDecl * Sema::createLambdaClosureType(SourceRange IntroducerRange, TypeSourceInfo *Info, unsigned LambdaDependencyKind, LambdaCaptureDefault CaptureDefault) { - DeclContext *DC = CurContext; + DeclContext *DC = CurContext->getEnclosingNonExpansionStatementContext(); bool IsGenericLambda = Info && getGenericLambdaTemplateParameterList(getCurLambda(), *this); @@ -1403,7 +1400,9 @@ void Sema::ActOnLambdaClosureQualifiers(LambdaIntroducer &Intro, // odr-use 'this' (in particular, in a default initializer for a non-static // data member). if (Intro.Default != LCD_None && - !LSI->Lambda->getParent()->isFunctionOrMethod() && + !LSI->Lambda->getParent() + ->getEnclosingNonExpansionStatementContext() + ->isFunctionOrMethod() && (getCurrentThisType().isNull() || CheckCXXThisCapture(SourceLocation(), /*Explicit=*/true, /*BuildAndDiagnose=*/false))) @@ -2551,9 +2550,12 @@ Sema::LambdaScopeForCallOperatorInstantiationRAII:: while (FDPattern && FD) { InstantiationAndPatterns.emplace_back(FDPattern, FD); - FDPattern = - dyn_cast<FunctionDecl>(getLambdaAwareParentOfDeclContext(FDPattern)); - FD = dyn_cast<FunctionDecl>(getLambdaAwareParentOfDeclContext(FD)); + FDPattern = dyn_cast<FunctionDecl>( + getLambdaAwareParentOfDeclContext(FDPattern) + ->getEnclosingNonExpansionStatementContext()); + FD = dyn_cast<FunctionDecl>( + getLambdaAwareParentOfDeclContext(FD) + ->getEnclosingNonExpansionStatementContext()); } // Add instantiated parameters and local vars to scopes, starting from the diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp index de53f6010a1b6..9502b440dbe97 100644 --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -4460,7 +4460,9 @@ LabelDecl *Sema::LookupExistingLabel(IdentifierInfo *II, SourceLocation Loc) { RedeclarationKind::NotForRedeclaration); // If we found a label, check to see if it is in the same context as us. // When in a Block, we don't want to reuse a label in an enclosing function. - if (!Res || Res->getDeclContext() != CurContext) + if (!Res || + Res->getDeclContext()->getEnclosingNonExpansionStatementContext() != + CurContext->getEnclosingNonExpansionStatementContext()) return nullptr; return cast<LabelDecl>(Res); } diff --git a/clang/lib/Sema/SemaStmtAttr.cpp b/clang/lib/Sema/SemaStmtAttr.cpp index f7de98a3e5cf0..740d2c6a6505c 100644 --- a/clang/lib/Sema/SemaStmtAttr.cpp +++ b/clang/lib/Sema/SemaStmtAttr.cpp @@ -38,6 +38,18 @@ static Attr *handleFallThroughAttr(Sema &S, Stmt *St, const ParsedAttr &A, return nullptr; } + // CWG 3045: The innermost enclosing switch statement of a fallthrough + // statement S shall be contained in the innermost enclosing expansion + // statement (8.7 [stmt.expand]) of S, if any. + for (Scope *Sc = S.getCurScope(); + Sc && !Sc->isFunctionScope() && !Sc->isSwitchScope(); + Sc = Sc->getParent()) { + if (Sc->isExpansionStmtScope()) { + S.Diag(A.getLoc(), diag::err_fallthrough_attr_invalid_placement); + return nullptr; + } + } + // If this is spelled as the standard C++17 attribute, but not in C++17, warn // about using it as an extension. if (!S.getLangOpts().CPlusPlus17 && A.isCXX11Attribute() && diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index cc24e03e77c07..d057476a012db 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -2101,6 +2101,11 @@ Decl *TemplateDeclInstantiator::VisitStaticAssertDecl(StaticAssertDecl *D) { InstantiatedMessageExpr.get(), D->getRParenLoc(), D->isFailed()); } +Decl *TemplateDeclInstantiator::VisitCXXExpansionStmtDecl( + CXXExpansionStmtDecl *OldESD) { + llvm_unreachable("TODO"); +} + Decl *TemplateDeclInstantiator::VisitEnumDecl(EnumDecl *D) { EnumDecl *PrevDecl = nullptr; if (EnumDecl *PatternPrev = getPreviousDeclForInstantiation(D)) { diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 8ae5df367e0dd..ad8e0b76f1dcb 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -9369,6 +9369,24 @@ TreeTransform<Derived>::TransformCXXForRangeStmt(CXXForRangeStmt *S) { return FinishCXXForRangeStmt(NewStmt.get(), Body.get()); } +template <typename Derived> +StmtResult TreeTransform<Derived>::TransformCXXExpansionStmtPattern( + CXXExpansionStmtPattern *S) { + llvm_unreachable("TOOD"); +} + +template <typename Derived> +StmtResult TreeTransform<Derived>::TransformCXXExpansionStmtInstantiation( + CXXExpansionStmtInstantiation *S) { + llvm_unreachable("TOOD"); +} + +template <typename Derived> +ExprResult TreeTransform<Derived>::TransformCXXExpansionSelectExpr( + CXXExpansionSelectExpr *E) { + llvm_unreachable("TOOD"); +} + template<typename Derived> StmtResult TreeTransform<Derived>::TransformMSDependentExistsStmt( diff --git a/clang/lib/Serialization/ASTCommon.cpp b/clang/lib/Serialization/ASTCommon.cpp index 69db02f2efc40..d07661a5b2f64 100644 --- a/clang/lib/Serialization/ASTCommon.cpp +++ b/clang/lib/Serialization/ASTCommon.cpp @@ -459,6 +459,7 @@ bool serialization::isRedeclarableDeclKind(unsigned Kind) { case Decl::HLSLRootSignature: case Decl::OpenACCDeclare: case Decl::OpenACCRoutine: + case Decl::CXXExpansionStmt: return false; // These indirectly derive from Redeclarable<T> but are not actually diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index b49bd5ea8bca6..e0b8589af33dd 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -405,6 +405,7 @@ class ASTDeclReader : public DeclVisitor<ASTDeclReader, void> { void VisitFriendDecl(FriendDecl *D); void VisitFriendTemplateDecl(FriendTemplateDecl *D); void VisitStaticAssertDecl(StaticAssertDecl *D); + void VisitCXXExpansionStmtDecl(CXXExpansionStmtDecl *D); void VisitBlockDecl(BlockDecl *BD); void VisitOutlinedFunctionDecl(OutlinedFunctionDecl *D); void VisitCapturedDecl(CapturedDecl *CD); @@ -2784,6 +2785,14 @@ void ASTDeclReader::VisitStaticAssertDecl(StaticAssertDecl *D) { D->RParenLoc = readSourceLocation(); } +void ASTDeclReader::VisitCXXExpansionStmtDecl(CXXExpansionStmtDecl *D) { + VisitDecl(D); + D->Pattern = cast<CXXExpansionStmtPattern>(Record.readStmt()); + D->Instantiations = + cast_or_null<CXXExpansionStmtInstantiation>(Record.readStmt()); + D->IndexNTTP = cast<NonTypeTemplateParmDecl>(Record.readDeclRef()); +} + void ASTDeclReader::VisitEmptyDecl(EmptyDecl *D) { VisitDecl(D); } @@ -4106,6 +4115,9 @@ Decl *ASTReader::ReadDeclRecord(GlobalDeclID ID) { case DECL_STATIC_ASSERT: D = StaticAssertDecl::CreateDeserialized(Context, ID); break; + case DECL_EXPANSION_STMT: + D = CXXExpansionStmtDecl::CreateDeserialized(Context, ID); + break; case DECL_OBJC_METHOD: D = ObjCMethodDecl::CreateDeserialized(Context, ID); break; diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp index 801eed43c2440..c929ea8ac8c82 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -1775,6 +1775,35 @@ void ASTStmtReader::VisitCXXForRangeStmt(CXXForRangeStmt *S) { S->setBody(Record.readSubStmt()); } +void ASTStmtReader::VisitCXXExpansionStmtPattern(CXXExpansionStmtPattern *S) { + VisitStmt(S); + Record.skipInts(1); // Skip kind. + S->LParenLoc = readSourceLocation(); + S->ColonLoc = readSourceLocation(); + S->RParenLoc = readSourceLocation(); + S->ParentDecl = cast<CXXExpansionStmtDecl>(Record.readDeclRef()); + for (Stmt *&SubStmt : S->children()) + SubStmt = Record.readSubStmt(); +} + +void ASTStmtReader::VisitCXXExpansionStmtInstantiation( + CXXExpansionStmtInstantiation *S) { + VisitStmt(S); + Record.skipInts(2); + S->BeginLoc = readSourceLocation(); + S->EndLoc = readSourceLocation(); + for (unsigned I = 0; I < S->getNumSubStmts(); ++I) + S->getAllSubStmts()[I] = Record.readSubStmt(); + S->setShouldApplyLifetimeExtensionToSharedStmts(Record.readBool()); +} + +void ASTStmtReader::VisitCXXExpansionSelectExpr( + CXXExpansionSelectExpr *E) { + VisitExpr(E); + E->setRangeExpr(cast<InitListExpr>(Record.readSubExpr())); + E->setIndexExpr(Record.readSubExpr()); +} + void ASTStmtReader::VisitMSDependentExistsStmt(MSDependentExistsStmt *S) { VisitStmt(S); S->KeywordLoc = readSourceLocation(); @@ -3623,6 +3652,19 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) { /*numHandlers=*/Record[ASTStmtReader::NumStmtFields]); break; + case STMT_CXX_EXPANSION_PATTERN: + S = CXXExpansionStmtPattern::CreateEmpty( + Context, Empty, + static_cast<CXXExpansionStmtPattern::ExpansionStmtKind>( + Record[ASTStmtReader::NumStmtFields])); + break; + + case STMT_CXX_EXPANSION_INSTANTIATION: + S = CXXExpansionStmtInstantiation::CreateEmpty( + Context, Empty, Record[ASTStmtReader::NumStmtFields], + Record[ASTStmtReader::NumStmtFields + 1]); + break; + case STMT_CXX_FOR_RANGE: S = new (Context) CXXForRangeStmt(Empty); break; @@ -4500,6 +4542,11 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) { S = new (Context) ConceptSpecializationExpr(Empty); break; } + + case EXPR_CXX_EXPANSION_SELECT: + S = new (Context) CXXExpansionSelectExpr(Empty); + break; + case STMT_OPENACC_COMPUTE_CONSTRUCT: { unsigned NumClauses = Record[ASTStmtReader::NumStmtFields]; S = OpenACCComputeConstruct::CreateEmpty(Context, NumClauses); diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp index 7646d5d5efe00..71971e8ae0fcd 100644 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -144,6 +144,7 @@ namespace clang { void VisitFriendDecl(FriendDecl *D); void VisitFriendTemplateDecl(FriendTemplateDecl *D); void VisitStaticAssertDecl(StaticAssertDecl *D); + void VisitCXXExpansionStmtDecl(CXXExpansionStmtDecl *D); void VisitBlockDecl(BlockDecl *D); void VisitOutlinedFunctionDecl(OutlinedFunctionDecl *D); void VisitCapturedDecl(CapturedDecl *D); @@ -2175,6 +2176,14 @@ void ASTDeclWriter::VisitStaticAssertDecl(StaticAssertDecl *D) { Code = serialization::DECL_STATIC_ASSERT; } +void ASTDeclWriter::VisitCXXExpansionStmtDecl(CXXExpansionStmtDecl *D) { + VisitDecl(D); + Record.AddStmt(D->getExpansionPattern()); + Record.AddStmt(D->getInstantiations()); + Record.AddDeclRef(D->getIndexTemplateParm()); + Code = serialization::DECL_EXPANSION_STMT; +} + /// Emit the DeclContext part of a declaration context decl. void ASTDeclWriter::VisitDeclContext(DeclContext *DC) { static_assert(DeclContext::NumDeclContextBits == 13, diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp index 934a95df1be7e..e33a715b143b8 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -1757,6 +1757,39 @@ void ASTStmtWriter::VisitCXXForRangeStmt(CXXForRangeStmt *S) { Code = serialization::STMT_CXX_FOR_RANGE; } +void ASTStmtWriter::VisitCXXExpansionStmtPattern(CXXExpansionStmtPattern *S) { + VisitStmt(S); + Record.push_back(static_cast<unsigned>(S->getKind())); + Record.AddSourceLocation(S->getLParenLoc()); + Record.AddSourceLocation(S->getColonLoc()); + Record.AddSourceLocation(S->getRParenLoc()); + Record.AddDeclRef(S->getDecl()); + for (Stmt* SubStmt : S->children()) + Record.AddStmt(SubStmt); + Code = serialization::STMT_CXX_EXPANSION_PATTERN; +} + +void ASTStmtWriter::VisitCXXExpansionStmtInstantiation( + CXXExpansionStmtInstantiation *S) { + VisitStmt(S); + Record.push_back(S->getInstantiations().size()); + Record.push_back(S->getSharedStmts().size()); + Record.AddSourceLocation(S->getBeginLoc()); + Record.AddSourceLocation(S->getEndLoc()); + for (Stmt *St : S->getAllSubStmts()) + Record.AddStmt(St); + Record.push_back(S->shouldApplyLifetimeExtensionToSharedStmts()); + Code = serialization::STMT_CXX_EXPANSION_INSTANTIATION; +} + +void ASTStmtWriter::VisitCXXExpansionSelectExpr( + CXXExpansionSelectExpr *E) { + VisitExpr(E); + Record.AddStmt(E->getRangeExpr()); + Record.AddStmt(E->getIndexExpr()); + Code = serialization::EXPR_CXX_EXPANSION_SELECT; +} + void ASTStmtWriter::VisitMSDependentExistsStmt(MSDependentExistsStmt *S) { VisitStmt(S); Record.AddSourceLocation(S->getKeywordLoc()); diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index e9522a7975515..76a682bf983cf 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1745,6 +1745,9 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::SEHExceptStmtClass: case Stmt::SEHLeaveStmtClass: case Stmt::SEHFinallyStmtClass: + case Stmt::CXXExpansionStmtPatternClass: + case Stmt::CXXExpansionStmtInstantiationClass: + case Stmt::CXXExpansionSelectExprClass: case Stmt::OMPCanonicalLoopClass: case Stmt::OMPParallelDirectiveClass: case Stmt::OMPSimdDirectiveClass: diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp index 3ee37ed2dfc27..a6d53af8e4eda 100644 --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -7282,6 +7282,7 @@ CXCursor clang_getCursorDefinition(CXCursor C) { case Decl::UnresolvedUsingIfExists: case Decl::OpenACCDeclare: case Decl::OpenACCRoutine: + case Decl::CXXExpansionStmt: return C; // Declaration kinds that don't make any sense here, but are diff --git a/clang/tools/libclang/CXCursor.cpp b/clang/tools/libclang/CXCursor.cpp index d31d2c0c9bb67..08624d4ce3f73 100644 --- a/clang/tools/libclang/CXCursor.cpp +++ b/clang/tools/libclang/CXCursor.cpp @@ -295,6 +295,8 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent, case Stmt::CoroutineBodyStmtClass: case Stmt::CoreturnStmtClass: + case Stmt::CXXExpansionStmtPatternClass: + case Stmt::CXXExpansionStmtInstantiationClass: K = CXCursor_UnexposedStmt; break; @@ -345,6 +347,7 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent, case Stmt::EmbedExprClass: case Stmt::HLSLOutArgExprClass: case Stmt::OpenACCAsteriskSizeExprClass: + case Stmt::CXXExpansionSelectExprClass: K = CXCursor_UnexposedExpr; break; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
