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/Decl.h b/clang/include/clang/AST/Decl.h index ee2321dd158d4..e15f4dcd3a0bc 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -1247,14 +1247,16 @@ class VarDecl : public DeclaratorDecl, public Redeclarable { /// 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(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(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..d30c197853ce5 100644 --- a/clang/include/clang/AST/DeclTemplate.h +++ b/clang/include/clang/AST/DeclTemplate.h @@ -3343,6 +3343,127 @@ 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. +/// +/// 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 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 *Expansion = 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 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() { 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()) return PD; diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h index 9435ab069a520..083576c5379bf 100644 --- a/clang/include/clang/AST/ExprCXX.h +++ b/clang/include/clang/AST/ExprCXX.h @@ -5499,6 +5499,52 @@ class BuiltinBitCastExpr final } }; +/// Helper that selects an expression from an InitListExpr depending +/// on the current expansion index. +/// +/// \see CXXExpansionStmtPattern +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(SubExprs[RANGE]); + } + + const InitListExpr *getRangeExpr() const { + return cast(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(SubExprs), + reinterpret_cast(SubExprs + COUNT)); + } + + const_child_range children() const { + return const_child_range( + reinterpret_cast(const_cast(SubExprs)), + reinterpret_cast(const_cast(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 8f427427d71ed..5502d68f99ad3 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,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..1caae321df54c 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,519 @@ 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]). +/// +/// The expression used to compute the size of the expansion is not stored and +/// is only created at the moment of expansion. +/// +/// Example: +/// \verbatim +/// static constexpr std::string_view foo = "1234"; +/// template for (auto x : foo) { +/// // ... +/// } +/// \endverbatim +/// +/// 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 declarations, with the number of +/// bindings and expansion size determined by the usual means (array size, +/// std::tuple_size, etc.). +/// +/// Example: +/// \verbatim +/// std::array a {1, 2, 3}; +/// template for (auto x : a) { +/// // ... +/// } +/// \endverbatim +/// +/// Sema wraps the initializer with a CXXExpansionSelectExpr, which selects a +/// binding based on the current expansion index. +/// +/// 4. Represents an expansion statement whose expansion-initializer is +/// type-dependent. +/// +/// This will be instantiated as either an iterating or destructuring expansion +/// statement. Dependent expansion statements can never be enumerating, even if +/// the expansion size is dependent because the expression-list contains a pack. +/// +/// Example: +/// \verbatim +/// template +/// void f() { +/// template for (auto x : T()) { +/// // ... +/// } +/// } +/// \endverbatim +/// +/// \see CXXExpansionStmtDecl for more documentation on expansion statements. +class CXXExpansionStmtPattern final + : public Stmt, + llvm::TrailingObjects { + 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; + + enum SubStmt { + INIT, + VAR, + BODY, + FIRST_CHILD_STMT, + COUNT_Enumerating = FIRST_CHILD_STMT, + + // Dependent expansion initializer. + EXPANSION_INITIALIZER = FIRST_CHILD_STMT, + COUNT_Dependent, + + // Destructuring expansion statement. + DECOMP_DECL = FIRST_CHILD_STMT, + COUNT_Destructuring, + + // Iterating expansion statement. + RANGE = FIRST_CHILD_STMT, + BEGIN, + END, + 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 *End, 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(this)->getExpansionVariable(); + } + + DeclStmt *getExpansionVarStmt() { return cast(getSubStmt(VAR)); } + const DeclStmt *getExpansionVarStmt() const { + return cast(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(getSubStmt(RANGE)); + } + + DeclStmt *getRangeVarStmt() { + assert(isIterating()); + return cast(getSubStmt(RANGE)); + } + + void setRangeVarStmt(DeclStmt *S) { + assert(isIterating()); + getSubStmt(RANGE) = S; + } + + const VarDecl *getRangeVar() const { + assert(isIterating()); + return cast(getRangeVarStmt()->getSingleDecl()); + } + + VarDecl *getRangeVar() { + assert(isIterating()); + return cast(getRangeVarStmt()->getSingleDecl()); + } + + const DeclStmt *getBeginVarStmt() const { + assert(isIterating()); + return cast(getSubStmt(BEGIN)); + } + + DeclStmt *getBeginVarStmt() { + assert(isIterating()); + return cast(getSubStmt(BEGIN)); + } + + void setBeginVarStmt(DeclStmt *S) { + assert(isIterating()); + getSubStmt(BEGIN) = S; + } + + const VarDecl *getBeginVar() const { + assert(isIterating()); + return cast(getBeginVarStmt()->getSingleDecl()); + } + + VarDecl *getBeginVar() { + assert(isIterating()); + return cast(getBeginVarStmt()->getSingleDecl()); + } + + const DeclStmt *getEndVarStmt() const { + assert(isIterating()); + return cast(getSubStmt(END)); + } + + DeclStmt *getEndVarStmt() { + assert(isIterating()); + return cast(getSubStmt(END)); + } + + void setEndVarStmt(DeclStmt *S) { + assert(isIterating()); + getSubStmt(END) = S; + } + + const VarDecl *getEndVar() const { + assert(isIterating()); + return cast(getEndVarStmt()->getSingleDecl()); + } + + VarDecl *getEndVar() { + assert(isIterating()); + return cast(getEndVarStmt()->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(this)->getDecompositionDecl(); + } + + // Accessors for dependent statements. + Expr *getExpansionInitializer() { + assert(isDependent()); + return cast(getSubStmt(EXPANSION_INITIALIZER)); + } + + const Expr *getExpansionInitializer() const { + assert(isDependent()); + return cast(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 + 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. +/// +/// { // Actual 'CompoundStmt'. +/// <1st instantiation> +/// } +/// ... +/// { // Actual 'CompoundStmt'. +/// +/// } +/// } +/// \endverbatim +/// +/// For example, the CXXExpansionStmtInstantiation that corresponds to the +/// following expansion statement +/// +/// \verbatim +/// std::tuple 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 { + 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 Instantiations, + ArrayRef SharedStmts, + bool ShouldApplyLifetimeExtensionToSharedStmts); + +public: + static CXXExpansionStmtInstantiation * + Create(ASTContext &C, SourceLocation BeginLoc, SourceLocation EndLoc, + ArrayRef Instantiations, ArrayRef SharedStmts, + bool ShouldApplyLifetimeExtensionToSharedStmts); + + static CXXExpansionStmtInstantiation *CreateEmpty(ASTContext &C, + EmptyShell Empty, + unsigned NumInstantiations, + unsigned NumSharedStmts); + + ArrayRef getAllSubStmts() const { + return getTrailingObjects(getNumSubStmts()); + } + + MutableArrayRef getAllSubStmts() { + return getTrailingObjects(getNumSubStmts()); + } + + unsigned getNumSubStmts() const { return NumInstantiations + NumSharedStmts; } + + ArrayRef getInstantiations() const { + return getTrailingObjects(NumInstantiations); + } + + ArrayRef 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 88ecd526e3d7e..499dc74d51099 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; def Friend : DeclNode; def FriendTemplate : DeclNode; def StaticAssert : DeclNode; +def CXXExpansionStmt : DeclNode, DeclContext; def Block : DeclNode, DeclContext; def OutlinedFunction : DeclNode, DeclContext; def Captured : DeclNode, DeclContext; diff --git a/clang/include/clang/Basic/StmtNodes.td b/clang/include/clang/Basic/StmtNodes.td index bf3686bb372d5..d51d4c6d23f7d 100644 --- a/clang/include/clang/Basic/StmtNodes.td +++ b/clang/include/clang/Basic/StmtNodes.td @@ -58,6 +58,11 @@ def CXXForRangeStmt : StmtNode; def CoroutineBodyStmt : StmtNode; def CoreturnStmt : StmtNode; +// C++ expansion statements (P1306) +def CXXExpansionStmtPattern : StmtNode; +def CXXExpansionStmtInstantiation + : StmtNode; // *Not* derived from CXXExpansionStmtPattern! + // Expressions def Expr : StmtNode; def PredefinedExpr : StmtNode; @@ -177,6 +182,9 @@ def CoyieldExpr : StmtNode; def ConceptSpecializationExpr : StmtNode; def RequiresExpr : StmtNode; +// C++26 Expansion statement support expressions +def CXXExpansionSelectExpr : StmtNode; + // Obj-C Expressions. def ObjCStringLiteral : StmtNode; def ObjCBoxedExpr : StmtNode; diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index d7d429eacd67a..ba5713d24a048 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -1460,6 +1460,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, @@ -1833,6 +1836,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, @@ -1924,6 +1933,7 @@ enum StmtCode { EXPR_CXX_FOLD, // CXXFoldExpr EXPR_CONCEPT_SPECIALIZATION, // ConceptSpecializationExpr EXPR_REQUIRES, // RequiresExpr + EXPR_CXX_EXPANSION_SELECT, // CXXExpansionSelectExpr // CUDA EXPR_CUDA_KERNEL_CALL, // CUDAKernelCallExpr diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index c1441744c8578..148a5b514b8c8 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 @@ -2852,6 +2857,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; @@ -7450,6 +7483,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 ToEnd = importChecked(Err, S->getEndVarStmt()); + if (Err) + return std::move(Err); + + return CXXExpansionStmtPattern::CreateIterating( + Importer.getToContext(), ToESD, ToInit, ToExpansionVar, ToRange, + ToBegin, ToEnd, 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 ToInstantiations; + SmallVector 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(); @@ -9346,6 +9454,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 30c6d3ed91f1e..3cbe309d75528 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(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(this)) + if (isa(this)) return true; if (const auto *Record = dyn_cast(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(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 47ae613b643b6..9e399b3a81ead 100644 --- a/clang/lib/AST/DeclPrinter.cpp +++ b/clang/lib/AST/DeclPrinter.cpp @@ -113,6 +113,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); @@ -1329,6 +1330,11 @@ void DeclPrinter::VisitClassTemplatePartialSpecializationDecl( VisitCXXRecordDecl(D); } +void DeclPrinter::VisitCXXExpansionStmtDecl(const CXXExpansionStmtDecl *D) { + D->getExpansionPattern()->printPretty(Out, nullptr, Policy, Indentation, "\n", + &Context); +} + //---------------------------------------------------------------------------- // Objective-C declarations //---------------------------------------------------------------------------- diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp index 2f7ae6d6cac63..a770112409486 100644 --- a/clang/lib/AST/DeclTemplate.cpp +++ b/clang/lib/AST/DeclTemplate.cpp @@ -1717,6 +1717,9 @@ clang::getReplacedTemplateParameter(Decl *D, unsigned Index) { return getReplacedTemplateParameter( cast(D)->getTemplateSpecializationInfo()->getTemplate(), Index); + case Decl::Kind::CXXExpansionStmt: + assert(Index == 0 && "expansion stmts only have a single template param"); + return {cast(D)->getIndexTemplateParm(), {}}; default: llvm_unreachable("Unhandled templated declaration kind"); } @@ -1788,3 +1791,22 @@ 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(nullptr, SourceLocation(), nullptr); +} + +SourceRange CXXExpansionStmtDecl::getSourceRange() const { + return Expansion ? Expansion->getSourceRange() : SourceRange(); +} diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index 340bb4b2ed6a3..2911b0da96bbf 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -3688,6 +3688,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 c7f0ff040194d..8898547e23c0f 100644 --- a/clang/lib/AST/ExprCXX.cpp +++ b/clang/lib/AST/ExprCXX.cpp @@ -2020,3 +2020,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 aeacd0dc765ef..3a1ed33e5e5f8 100644 --- a/clang/lib/AST/ExprClassification.cpp +++ b/clang/lib/AST/ExprClassification.cpp @@ -216,6 +216,7 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { case Expr::SourceLocExprClass: case Expr::ConceptSpecializationExprClass: case Expr::RequiresExprClass: + 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 3b91678f7d400..7ab4f3ce9ffaf 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -20276,6 +20276,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::InitListExprClass: { diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index 5572e0a7ae59c..b3866c0de11f7 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -42,7 +42,7 @@ using namespace clang; namespace { static bool isLocalContainerContext(const DeclContext *DC) { - return isa(DC) || isa(DC) || isa(DC); + return isa(DC); } static const FunctionDecl *getStructor(const FunctionDecl *fn) { @@ -1851,6 +1851,8 @@ static GlobalDecl getParentOfLocalEntity(const DeclContext *DC) { GD = GlobalDecl(CD, Ctor_Complete); else if (auto *DD = dyn_cast(DC)) GD = GlobalDecl(DD, Dtor_Complete); + else if (DC->isExpansionStmt()) + GD = getParentOfLocalEntity(DC->getEnclosingNonExpansionStatementContext()); else GD = GlobalDecl(cast(DC)); return GD; @@ -2191,6 +2193,9 @@ void CXXNameMangler::manglePrefix(const DeclContext *DC, bool NoFunction) { if (NoFunction && isLocalContainerContext(DC)) return; + if (DC->isExpansionStmt()) + return; + const NamedDecl *ND = cast(DC); if (mangleSubstitution(ND)) return; @@ -4940,6 +4945,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..6f7cddd490e8d 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(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 +CXXExpansionStmtPattern *CXXExpansionStmtPattern::AllocateAndConstruct( + ASTContext &Context, ExpansionStmtKind Kind, Args &&...Arguments) { + std::size_t Size = totalSizeToAlloc(getNumSubStmts(Kind)); + void *Mem = Context.Allocate(Size, alignof(CXXExpansionStmtPattern)); + return new (Mem) + CXXExpansionStmtPattern(Kind, std::forward(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 *End, + 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->setEndVarStmt(End); + return Pattern; +} + +SourceLocation CXXExpansionStmtPattern::getBeginLoc() const { + return ParentDecl->getLocation(); +} + +DecompositionDecl * +CXXExpansionStmtPattern::getDecompositionDecl() { + assert(isDestructuring()); + return cast( + cast(getDecompositionDeclStmt())->getSingleDecl()); +} + +VarDecl *CXXExpansionStmtPattern::getExpansionVariable() { + Decl *LV = cast(getExpansionVarStmt())->getSingleDecl(); + assert(LV && "No expansion variable in CXXExpansionStmtPattern"); + return cast(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 Instantiations, ArrayRef 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 Instantiations, ArrayRef SharedStmts, + bool ShouldApplyLifetimeExtensionToSharedStmts) { + void *Mem = C.Allocate( + totalSizeToAlloc(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(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 ff8ca01ec5477..6032e40ec5ca5 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(Node->getSingleDecl())) + !isa( + 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 4a8c638c85331..3500d39e0c703 100644 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -364,6 +364,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()); @@ -2400,6 +2411,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::VisitObjCStringLiteral(const ObjCStringLiteral *S) { diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp index 41aebdb8d2f1b..a88d152c4c507 100644 --- a/clang/lib/AST/TextNodeDumper.cpp +++ b/clang/lib/AST/TextNodeDumper.cpp @@ -951,7 +951,11 @@ void TextNodeDumper::dumpBareDeclRef(const Decl *D) { switch (ND->getKind()) { case Decl::Decomposition: { auto *DD = cast(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: { @@ -1495,6 +1499,30 @@ void clang::TextNodeDumper::VisitCoreturnStmt(const CoreturnStmt *Node) { OS << " implicit"; } +void TextNodeDumper::VisitCXXExpansionStmtPattern( + const CXXExpansionStmtPattern *Node) { + switch (Node->getKind()) { + case CXXExpansionStmtPattern::ExpansionStmtKind::Enumerating: + OS << " enumerating"; + break; + case CXXExpansionStmtPattern::ExpansionStmtKind::Iterating: + OS << " iterating"; + break; + case CXXExpansionStmtPattern::ExpansionStmtKind::Destructuring: + OS << " destructuring"; + break; + case CXXExpansionStmtPattern::ExpansionStmtKind::Dependent: + OS << " dependent"; + break; + } +} + +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 8b1cd83af2396..7c96e38908267 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(D)); diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp index 36be3295950b8..6f02ccd99c0e4 100644 --- a/clang/lib/CodeGen/CGStmt.cpp +++ b/clang/lib/CodeGen/CGStmt.cpp @@ -204,6 +204,10 @@ void CodeGenFunction::EmitStmt(const Stmt *S, ArrayRef Attrs) { case Stmt::CXXForRangeStmtClass: EmitCXXForRangeStmt(cast(*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(*S)); break; diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index 46addea232b03..5fbfb4023ee4d 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -1632,14 +1632,15 @@ DeclContext *Sema::getFunctionLevelDeclContext(bool AllowLambda) const { DeclContext *DC = CurContext; while (true) { - if (isa(DC) || isa(DC) || isa(DC) || - isa(DC)) { + if (isa(DC)) { DC = DC->getParent(); } else if (!AllowLambda && isa(DC) && cast(DC)->getOverloadedOperator() == OO_Call && cast(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 651437a6f4c30..468376039fae5 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -7437,7 +7437,7 @@ static bool shouldConsiderLinkage(const VarDecl *VD) { if (DC->getDeclKind() == Decl::HLSLBuffer) return false; - if (isa(DC)) + if (isa(DC)) return false; llvm_unreachable("Unexpected context"); } @@ -7447,7 +7447,7 @@ static bool shouldConsiderLinkage(const FunctionDecl *FD) { if (DC->isFileContext() || DC->isFunctionOrMethod() || isa(DC) || isa(DC)) return true; - if (DC->isRecord()) + if (DC->isRecord() || isa(DC)) return false; llvm_unreachable("Unexpected context"); } diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp index a0483c3027199..53603b76011ae 100644 --- a/clang/lib/Sema/SemaExceptionSpec.cpp +++ b/clang/lib/Sema/SemaExceptionSpec.cpp @@ -1288,6 +1288,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: @@ -1538,6 +1539,13 @@ CanThrowResult Sema::canThrow(const Stmt *S) { case Stmt::SEHTryStmtClass: case Stmt::SwitchStmtClass: case Stmt::WhileStmtClass: + case Stmt::CXXExpansionStmtInstantiationClass: + return canSubStmtsThrow(*this, S); + + case Stmt::CXXExpansionStmtPatternClass: + if (auto *Pattern = cast(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 d3c2cc559ea20..7fc6d3cff36b0 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -19300,11 +19300,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 43bcb4f743cfa..0d7e2a9c5b324 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -7600,7 +7600,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(DC)) diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp index f65c55a209622..c618f4e0a70db 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(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); @@ -1376,7 +1373,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))) @@ -2519,9 +2518,12 @@ Sema::LambdaScopeForCallOperatorInstantiationRAII:: while (FDPattern && FD) { InstantiationAndPatterns.emplace_back(FDPattern, FD); - FDPattern = - dyn_cast(getLambdaAwareParentOfDeclContext(FDPattern)); - FD = dyn_cast(getLambdaAwareParentOfDeclContext(FD)); + FDPattern = dyn_cast( + getLambdaAwareParentOfDeclContext(FDPattern) + ->getEnclosingNonExpansionStatementContext()); + FD = dyn_cast( + 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 5915d6e57d893..88dcd27d45ad2 100644 --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -4455,7 +4455,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(Res); } diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 26693514bb278..f8136c3c24a52 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -2087,6 +2087,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 0e8b674a006d0..df25b1701207f 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -9292,6 +9292,24 @@ TreeTransform::TransformCXXForRangeStmt(CXXForRangeStmt *S) { return FinishCXXForRangeStmt(NewStmt.get(), Body.get()); } +template +StmtResult TreeTransform::TransformCXXExpansionStmtPattern( + CXXExpansionStmtPattern *S) { + llvm_unreachable("TOOD"); +} + +template +StmtResult TreeTransform::TransformCXXExpansionStmtInstantiation( + CXXExpansionStmtInstantiation *S) { + llvm_unreachable("TOOD"); +} + +template +ExprResult TreeTransform::TransformCXXExpansionSelectExpr( + CXXExpansionSelectExpr *E) { + llvm_unreachable("TOOD"); +} + template StmtResult TreeTransform::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 but are not actually diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index 5456e73956659..e8da8a3060904 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -405,6 +405,7 @@ class ASTDeclReader : public DeclVisitor { 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); @@ -2769,6 +2770,14 @@ void ASTDeclReader::VisitStaticAssertDecl(StaticAssertDecl *D) { D->RParenLoc = readSourceLocation(); } +void ASTDeclReader::VisitCXXExpansionStmtDecl(CXXExpansionStmtDecl *D) { + VisitDecl(D); + D->Expansion = cast(Record.readStmt()); + D->Instantiations = + cast_or_null(Record.readStmt()); + D->IndexNTTP = cast(Record.readDeclRef()); +} + void ASTDeclReader::VisitEmptyDecl(EmptyDecl *D) { VisitDecl(D); } @@ -4083,6 +4092,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 eef97a8588f0b..5c65be8c594ef 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -1730,6 +1730,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(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(Record.readSubExpr())); + E->setIndexExpr(Record.readSubExpr()); +} + void ASTStmtReader::VisitMSDependentExistsStmt(MSDependentExistsStmt *S) { VisitStmt(S); S->KeywordLoc = readSourceLocation(); @@ -3566,6 +3595,19 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) { /*numHandlers=*/Record[ASTStmtReader::NumStmtFields]); break; + case STMT_CXX_EXPANSION_PATTERN: + S = CXXExpansionStmtPattern::CreateEmpty( + Context, Empty, + static_cast( + 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; @@ -4443,6 +4485,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 c9f8797ab973f..c65c9f8fc581f 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); @@ -2188,6 +2189,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 acf345392aa1a..2b223d1f15c53 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -1704,6 +1704,39 @@ void ASTStmtWriter::VisitCXXForRangeStmt(CXXForRangeStmt *S) { Code = serialization::STMT_CXX_FOR_RANGE; } +void ASTStmtWriter::VisitCXXExpansionStmtPattern(CXXExpansionStmtPattern *S) { + VisitStmt(S); + Record.push_back(static_cast(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 a759aee47b8ea..f7cf8c2b0b777 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1752,6 +1752,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 f4d6fa72a1dfe..d4da1cc8ba722 100644 --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -7254,6 +7254,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 0a43d73063c1f..264a79f5d3a48 100644 --- a/clang/tools/libclang/CXCursor.cpp +++ b/clang/tools/libclang/CXCursor.cpp @@ -290,6 +290,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; @@ -338,6 +340,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;