diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index 495f2ab3926ce..e0693b5f36e62 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -1784,6 +1784,7 @@ Pack Indexing __cpp_pack_indexing C ``= delete ("should have a reason");`` __cpp_deleted_function C++26 C++03 Variadic Friends __cpp_variadic_friend C++26 C++03 Trivial Relocatability __cpp_trivial_relocatability C++26 C++03 +Expansion Statements C++26 C++03 --------------------------------------------- -------------------------------- ------------- ------------- Designated initializers (N494) C99 C89 ``_Complex`` (N693) C99 C89, C++ diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index add1582344a0e..b2c518e9adf64 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -171,6 +171,8 @@ C++2c Feature Support At this timem, references to constexpr and decomposition of *tuple-like* types are not supported (only arrays and aggregates are). +- Implemented `P1306R5 `_ Expansion Statements. + C++23 Feature Support ^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/AST/ASTNodeTraverser.h b/clang/include/clang/AST/ASTNodeTraverser.h index e74bb72571d64..de0aa7f62ad68 100644 --- a/clang/include/clang/AST/ASTNodeTraverser.h +++ b/clang/include/clang/AST/ASTNodeTraverser.h @@ -959,6 +959,12 @@ class ASTNodeTraverser } } + void VisitExpansionStmtDecl(const ExpansionStmtDecl *Node) { + Visit(Node->getExpansionPattern()); + if (Traversal != TK_IgnoreUnlessSpelledInSource) + Visit(Node->getInstantiations()); + } + void VisitCallExpr(const CallExpr *Node) { for (const auto *Child : make_filter_range(Node->children(), [this](const Stmt *Child) { diff --git a/clang/include/clang/AST/ComputeDependence.h b/clang/include/clang/AST/ComputeDependence.h index c298f2620f211..792f45bea5aeb 100644 --- a/clang/include/clang/AST/ComputeDependence.h +++ b/clang/include/clang/AST/ComputeDependence.h @@ -94,6 +94,7 @@ class DesignatedInitExpr; class ParenListExpr; class PseudoObjectExpr; class AtomicExpr; +class CXXExpansionInitListExpr; class ArraySectionExpr; class OMPArrayShapingExpr; class OMPIteratorExpr; @@ -191,6 +192,8 @@ ExprDependence computeDependence(ParenListExpr *E); ExprDependence computeDependence(PseudoObjectExpr *E); ExprDependence computeDependence(AtomicExpr *E); +ExprDependence computeDependence(CXXExpansionInitListExpr *E); + ExprDependence computeDependence(ArraySectionExpr *E); ExprDependence computeDependence(OMPArrayShapingExpr *E); ExprDependence computeDependence(OMPIteratorExpr *E); diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index 406d79ebd6641..575bd4d160882 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -1254,7 +1254,9 @@ class VarDecl : public DeclaratorDecl, public Redeclarable { 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 c6326a8ba506d..b26d10c698952 100644 --- a/clang/include/clang/AST/DeclBase.h +++ b/clang/include/clang/AST/DeclBase.h @@ -2195,6 +2195,8 @@ class DeclContext { return getDeclKind() == Decl::RequiresExprBody; } + bool isExpansionStmt() const { return getDeclKind() == Decl::ExpansionStmt; } + bool isNamespace() const { return getDeclKind() == Decl::Namespace; } bool isStdNamespace() const; @@ -2292,6 +2294,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..7edc8e80da825 100644 --- a/clang/include/clang/AST/DeclTemplate.h +++ b/clang/include/clang/AST/DeclTemplate.h @@ -3343,6 +3343,55 @@ 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. +class ExpansionStmtDecl : public Decl, public DeclContext { + CXXExpansionStmt *Expansion = nullptr; + TemplateParameterList *TParams; + CXXExpansionInstantiationStmt *Instantiations = nullptr; + + ExpansionStmtDecl(DeclContext *DC, SourceLocation Loc, + TemplateParameterList *TParams); + +public: + friend class ASTDeclReader; + + static ExpansionStmtDecl *Create(ASTContext &C, DeclContext *DC, + SourceLocation Loc, + TemplateParameterList *TParams); + static ExpansionStmtDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID); + + CXXExpansionStmt *getExpansionPattern() { return Expansion; } + const CXXExpansionStmt *getExpansionPattern() const { return Expansion; } + void setExpansionPattern(CXXExpansionStmt *S) { Expansion = S; } + + CXXExpansionInstantiationStmt *getInstantiations() { return Instantiations; } + const CXXExpansionInstantiationStmt *getInstantiations() const { + return Instantiations; + } + + void setInstantiations(CXXExpansionInstantiationStmt *S) { + Instantiations = S; + } + + NonTypeTemplateParmDecl *getIndexTemplateParm() const { + return cast(TParams->getParam(0)); + } + TemplateParameterList *getTemplateParameters() const { return TParams; } + + SourceRange getSourceRange() const override LLVM_READONLY; + + static bool classof(const Decl *D) { return classofKind(D->getKind()); } + static bool classofKind(Kind K) { return K == ExpansionStmt; } +}; + 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 d78c7b6363b5d..580523ed8d74c 100644 --- a/clang/include/clang/AST/ExprCXX.h +++ b/clang/include/clang/AST/ExprCXX.h @@ -5501,6 +5501,156 @@ class BuiltinBitCastExpr final } }; +/// Represents an expansion-init-list to be expanded over by an expansion +/// statement. +class CXXExpansionInitListExpr final + : public Expr, + llvm::TrailingObjects { + friend class ASTStmtReader; + friend TrailingObjects; + + const unsigned NumExprs; + SourceLocation LBraceLoc; + SourceLocation RBraceLoc; + + CXXExpansionInitListExpr(EmptyShell ES, unsigned NumExprs); + CXXExpansionInitListExpr(ArrayRef Exprs, SourceLocation LBraceLoc, + SourceLocation RBraceLoc); + +public: + static CXXExpansionInitListExpr *Create(const ASTContext &C, + ArrayRef Exprs, + SourceLocation LBraceLoc, + SourceLocation RBraceLoc); + + static CXXExpansionInitListExpr * + CreateEmpty(const ASTContext &C, EmptyShell Empty, unsigned NumExprs); + + ArrayRef getExprs() const { return getTrailingObjects(NumExprs); } + MutableArrayRef getExprs() { return getTrailingObjects(NumExprs); } + unsigned getNumExprs() const { return NumExprs; } + + bool containsPackExpansion() const; + + SourceLocation getBeginLoc() const { return getLBraceLoc(); } + SourceLocation getEndLoc() const { return getRBraceLoc(); } + + SourceLocation getLBraceLoc() const { return LBraceLoc; } + SourceLocation getRBraceLoc() const { return RBraceLoc; } + + child_range children() { + const_child_range CCR = + const_cast(this)->children(); + return child_range(cast_away_const(CCR.begin()), + cast_away_const(CCR.end())); + } + + const_child_range children() const { + Stmt **Stmts = getTrailingStmts(); + return const_child_range(Stmts, Stmts + NumExprs); + } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == CXXExpansionInitListExprClass; + } + +private: + Stmt **getTrailingStmts() const { + return reinterpret_cast(const_cast(getTrailingObjects())); + } +}; + +/// Helper that selects an expression from an expansion init list depending +/// on the current expansion index. +class CXXExpansionInitListSelectExpr : public Expr { + friend class ASTStmtReader; + + enum SubExpr { RANGE, INDEX, COUNT }; + Expr *SubExprs[COUNT]; + +public: + CXXExpansionInitListSelectExpr(EmptyShell Empty); + CXXExpansionInitListSelectExpr(const ASTContext &C, + CXXExpansionInitListExpr *Range, Expr *Idx); + + CXXExpansionInitListExpr *getRangeExpr() { + return cast(SubExprs[RANGE]); + } + + const CXXExpansionInitListExpr *getRangeExpr() const { + return cast(SubExprs[RANGE]); + } + + void setRangeExpr(CXXExpansionInitListExpr *E) { SubExprs[RANGE] = E; } + + Expr *getIndexExpr() { return SubExprs[INDEX]; } + const Expr *getIndexExpr() const { return SubExprs[INDEX]; } + void setIndexExpr(Expr *E) { SubExprs[INDEX] = E; } + + SourceLocation getBeginLoc() const { return getRangeExpr()->getBeginLoc(); } + SourceLocation getEndLoc() const { return getRangeExpr()->getEndLoc(); } + + child_range children() { + return child_range(reinterpret_cast(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() == CXXExpansionInitListSelectExprClass; + } +}; + +class CXXDestructuringExpansionSelectExpr : public Expr { + friend class ASTStmtReader; + + DecompositionDecl *Decomposition; + Expr *Index; + +public: + CXXDestructuringExpansionSelectExpr(EmptyShell Empty); + CXXDestructuringExpansionSelectExpr(const ASTContext &C, + DecompositionDecl *Decomposition, + Expr *Index); + + DecompositionDecl *getDecompositionDecl() { + return cast(Decomposition); + } + + const DecompositionDecl *getDecompositionDecl() const { + return cast(Decomposition); + } + + void setDecompositionDecl(DecompositionDecl *E) { Decomposition = E; } + + Expr *getIndexExpr() { return Index; } + const Expr *getIndexExpr() const { return Index; } + void setIndexExpr(Expr *E) { Index = E; } + + SourceLocation getBeginLoc() const { return Decomposition->getBeginLoc(); } + SourceLocation getEndLoc() const { return Decomposition->getEndLoc(); } + + child_range children() { + return child_range(reinterpret_cast(&Index), + reinterpret_cast(&Index + 1)); + } + + const_child_range children() const { + return const_child_range( + reinterpret_cast(const_cast(&Index)), + reinterpret_cast(const_cast(&Index + 1))); + } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == CXXDestructuringExpansionSelectExprClass; + } +}; + } // namespace clang #endif // LLVM_CLANG_AST_EXPRCXX_H diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index 32b2b6bdb989c..7941b15a79806 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(ExpansionStmtDecl, { + if (D->getInstantiations() && + getDerived().shouldVisitTemplateInstantiations()) + TRY_TO(TraverseStmt(D->getInstantiations())); + + TRY_TO(TraverseStmt(D->getExpansionPattern())); +}) + DEF_TRAVERSE_DECL(OMPThreadPrivateDecl, { for (auto *I : D->varlist()) { TRY_TO(TraverseStmt(I)); @@ -3117,6 +3125,15 @@ DEF_TRAVERSE_STMT(RequiresExpr, { TRY_TO(TraverseConceptRequirement(Req)); }) +DEF_TRAVERSE_STMT(CXXEnumeratingExpansionStmt, {}) +DEF_TRAVERSE_STMT(CXXIteratingExpansionStmt, {}) +DEF_TRAVERSE_STMT(CXXDestructuringExpansionStmt, {}) +DEF_TRAVERSE_STMT(CXXDependentExpansionStmt, {}) +DEF_TRAVERSE_STMT(CXXExpansionInstantiationStmt, {}) +DEF_TRAVERSE_STMT(CXXExpansionInitListExpr, {}) +DEF_TRAVERSE_STMT(CXXExpansionInitListSelectExpr, {}) +DEF_TRAVERSE_STMT(CXXDestructuringExpansionSelectExpr, {}) + // These literals (all of them) do not need any action. DEF_TRAVERSE_STMT(IntegerLiteral, {}) DEF_TRAVERSE_STMT(FixedPointLiteral, {}) diff --git a/clang/include/clang/AST/StmtCXX.h b/clang/include/clang/AST/StmtCXX.h index 5d68d3ef64a20..087f7f10c1285 100644 --- a/clang/include/clang/AST/StmtCXX.h +++ b/clang/include/clang/AST/StmtCXX.h @@ -22,6 +22,7 @@ namespace clang { class VarDecl; +class ExpansionStmtDecl; /// CXXCatchStmt - This represents a C++ catch block. /// @@ -524,6 +525,354 @@ class CoreturnStmt : public Stmt { } }; +/// CXXExpansionStmt - Base class for an unexpanded C++ expansion statement. +class CXXExpansionStmt : public Stmt { + friend class ASTStmtReader; + + ExpansionStmtDecl *ParentDecl; + SourceLocation ForLoc; + SourceLocation LParenLoc; + SourceLocation ColonLoc; + SourceLocation RParenLoc; + +protected: + enum SubStmt { + INIT, + VAR, + BODY, + FIRST_CHILD_STMT, + + // CXXDependentExpansionStmt + EXPANSION_INITIALIZER = FIRST_CHILD_STMT, + COUNT_CXXDependentExpansionStmt, + + // CXXDestructuringExpansionStmt + DECOMP_DECL = FIRST_CHILD_STMT, + COUNT_CXXDestructuringExpansionStmt, + + // CXXIteratingExpansionStmt + RANGE = FIRST_CHILD_STMT, + BEGIN, + END, + COUNT_CXXIteratingExpansionStmt, + + MAX_COUNT = COUNT_CXXIteratingExpansionStmt, + }; + + // Managing the memory for this properly would be rather complicated, and + // expansion statements are fairly uncommon, so just allocate space for the + // maximum amount of substatements we could possibly have. + Stmt *SubStmts[MAX_COUNT]; + + CXXExpansionStmt(StmtClass SC, EmptyShell Empty); + CXXExpansionStmt(StmtClass SC, ExpansionStmtDecl *ESD, Stmt *Init, + DeclStmt *ExpansionVar, SourceLocation ForLoc, + SourceLocation LParenLoc, SourceLocation ColonLoc, + SourceLocation RParenLoc); + +public: + SourceLocation getForLoc() const { return ForLoc; } + SourceLocation getLParenLoc() const { return LParenLoc; } + SourceLocation getColonLoc() const { return ColonLoc; } + SourceLocation getRParenLoc() const { return RParenLoc; } + + SourceLocation getBeginLoc() const; + SourceLocation getEndLoc() const { + return getBody() ? getBody()->getEndLoc() : RParenLoc; + } + + bool hasDependentSize() const; + + ExpansionStmtDecl *getDecl() { return ParentDecl; } + const ExpansionStmtDecl *getDecl() const { return ParentDecl; } + + Stmt *getInit() { return SubStmts[INIT]; } + const Stmt *getInit() const { return SubStmts[INIT]; } + void setInit(Stmt *S) { SubStmts[INIT] = S; } + + VarDecl *getExpansionVariable(); + const VarDecl *getExpansionVariable() const { + return const_cast(this)->getExpansionVariable(); + } + + DeclStmt *getExpansionVarStmt() { return cast(SubStmts[VAR]); } + const DeclStmt *getExpansionVarStmt() const { + return cast(SubStmts[VAR]); + } + + void setExpansionVarStmt(Stmt *S) { SubStmts[VAR] = S; } + + Stmt *getBody() { return SubStmts[BODY]; } + const Stmt *getBody() const { return SubStmts[BODY]; } + void setBody(Stmt *S) { SubStmts[BODY] = S; } + + static bool classof(const Stmt *T) { + return T->getStmtClass() >= firstCXXExpansionStmtConstant && + T->getStmtClass() <= lastCXXExpansionStmtConstant; + } + + child_range children() { + return child_range(SubStmts, SubStmts + FIRST_CHILD_STMT); + } + + const_child_range children() const { + return const_child_range(SubStmts, SubStmts + FIRST_CHILD_STMT); + } +}; + +/// Represents an unexpanded enumerating expansion statement. +/// +/// The expansion initializer of this is always a CXXExpansionInitListExpr. +class CXXEnumeratingExpansionStmt : public CXXExpansionStmt { + friend class ASTStmtReader; + +public: + CXXEnumeratingExpansionStmt(EmptyShell Empty); + CXXEnumeratingExpansionStmt(ExpansionStmtDecl *ESD, Stmt *Init, + DeclStmt *ExpansionVar, SourceLocation ForLoc, + SourceLocation LParenLoc, SourceLocation ColonLoc, + SourceLocation RParenLoc); + + static bool classof(const Stmt *T) { + return T->getStmtClass() == CXXEnumeratingExpansionStmtClass; + } +}; + +/// Represents an expansion statement whose expansion-initializer is dependent. +class CXXDependentExpansionStmt : public CXXExpansionStmt { + friend class ASTStmtReader; + +public: + CXXDependentExpansionStmt(EmptyShell Empty); + CXXDependentExpansionStmt(ExpansionStmtDecl *ESD, Stmt *Init, + DeclStmt *ExpansionVar, Expr *ExpansionInitializer, + SourceLocation ForLoc, SourceLocation LParenLoc, + SourceLocation ColonLoc, SourceLocation RParenLoc); + + Expr *getExpansionInitializer() { + return cast(SubStmts[EXPANSION_INITIALIZER]); + } + const Expr *getExpansionInitializer() const { + return cast(SubStmts[EXPANSION_INITIALIZER]); + } + void setExpansionInitializer(Expr *S) { SubStmts[EXPANSION_INITIALIZER] = S; } + + child_range children() { + return child_range(SubStmts, SubStmts + COUNT_CXXDependentExpansionStmt); + } + + const_child_range children() const { + return const_child_range(SubStmts, + SubStmts + COUNT_CXXDependentExpansionStmt); + } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == CXXDependentExpansionStmtClass; + } +}; + +/// Represents an unexpanded iterating expansion statement. +/// +/// The expression used to compute the size of the expansion is not stored in +/// this as it is only created at the moment of expansion. +class CXXIteratingExpansionStmt : public CXXExpansionStmt { + friend class ASTStmtReader; + +public: + CXXIteratingExpansionStmt(EmptyShell Empty); + CXXIteratingExpansionStmt(ExpansionStmtDecl *ESD, Stmt *Init, + DeclStmt *ExpansionVar, DeclStmt *Range, + DeclStmt *Begin, DeclStmt *End, + SourceLocation ForLoc, SourceLocation LParenLoc, + SourceLocation ColonLoc, SourceLocation RParenLoc); + + const DeclStmt *getRangeVarStmt() const { + return cast(SubStmts[RANGE]); + } + DeclStmt *getRangeVarStmt() { return cast(SubStmts[RANGE]); } + void setRangeVarStmt(DeclStmt *S) { SubStmts[RANGE] = S; } + + const VarDecl *getRangeVar() const { + return cast(getRangeVarStmt()->getSingleDecl()); + } + + VarDecl *getRangeVar() { + return cast(getRangeVarStmt()->getSingleDecl()); + } + + const DeclStmt *getBeginVarStmt() const { + return cast(SubStmts[BEGIN]); + } + DeclStmt *getBeginVarStmt() { return cast(SubStmts[BEGIN]); } + void setBeginVarStmt(DeclStmt *S) { SubStmts[BEGIN] = S; } + + const VarDecl *getBeginVar() const { + return cast(getBeginVarStmt()->getSingleDecl()); + } + + VarDecl *getBeginVar() { + return cast(getBeginVarStmt()->getSingleDecl()); + } + + const DeclStmt *getEndVarStmt() const { + return cast(SubStmts[END]); + } + DeclStmt *getEndVarStmt() { return cast(SubStmts[END]); } + void setEndVarStmt(DeclStmt *S) { SubStmts[END] = S; } + + const VarDecl *getEndVar() const { + return cast(getEndVarStmt()->getSingleDecl()); + } + + VarDecl *getEndVar() { + return cast(getEndVarStmt()->getSingleDecl()); + } + + child_range children() { + return child_range(SubStmts, SubStmts + COUNT_CXXIteratingExpansionStmt); + } + + const_child_range children() const { + return const_child_range(SubStmts, + SubStmts + COUNT_CXXIteratingExpansionStmt); + } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == CXXIteratingExpansionStmtClass; + } +}; + +/// Represents an expansion statement whose expansion-initializer is dependent. +class CXXDestructuringExpansionStmt : public CXXExpansionStmt { + friend class ASTStmtReader; + +public: + CXXDestructuringExpansionStmt(EmptyShell Empty); + CXXDestructuringExpansionStmt(ExpansionStmtDecl *ESD, Stmt *Init, + DeclStmt *ExpansionVar, + Stmt *DecompositionDeclStmt, + SourceLocation ForLoc, SourceLocation LParenLoc, + SourceLocation ColonLoc, + SourceLocation RParenLoc); + + Stmt *getDecompositionDeclStmt() { return SubStmts[DECOMP_DECL]; } + const Stmt *getDecompositionDeclStmt() const { return SubStmts[DECOMP_DECL]; } + void setDecompositionDeclStmt(Stmt *S) { SubStmts[DECOMP_DECL] = S; } + + DecompositionDecl *getDecompositionDecl(); + const DecompositionDecl *getDecompositionDecl() const { + return const_cast(this) + ->getDecompositionDecl(); + } + + child_range children() { + return child_range(SubStmts, + SubStmts + COUNT_CXXDestructuringExpansionStmt); + } + + const_child_range children() const { + return const_child_range(SubStmts, + SubStmts + COUNT_CXXDestructuringExpansionStmt); + } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == CXXDestructuringExpansionStmtClass; + } +}; + +/// Represents the code generated for an instantiated expansion statement. +/// +/// This holds 'shared statements' and 'instantiations'; these encode the +/// general underlying pattern that all expansion statements desugar to: +/// +/// \verbatim +/// { +/// +/// { +/// <1st instantiation> +/// } +/// ... +/// { +/// +/// } +/// } +/// \endverbatim +class CXXExpansionInstantiationStmt 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; + + CXXExpansionInstantiationStmt(EmptyShell Empty, unsigned NumInstantiations, + unsigned NumSharedStmts); + CXXExpansionInstantiationStmt(SourceLocation BeginLoc, SourceLocation EndLoc, + ArrayRef Instantiations, + ArrayRef SharedStmts, + bool ShouldApplyLifetimeExtensionToSharedStmts); + +public: + static CXXExpansionInstantiationStmt * + Create(ASTContext &C, SourceLocation BeginLoc, SourceLocation EndLoc, + ArrayRef Instantiations, ArrayRef SharedStmts, + bool ShouldApplyLifetimeExtensionToSharedStmts); + + static CXXExpansionInstantiationStmt *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() == CXXExpansionInstantiationStmtClass; + } +}; + } // end namespace clang #endif diff --git a/clang/include/clang/AST/TextNodeDumper.h b/clang/include/clang/AST/TextNodeDumper.h index 88ecd526e3d7e..4355e7fbab9b2 100644 --- a/clang/include/clang/AST/TextNodeDumper.h +++ b/clang/include/clang/AST/TextNodeDumper.h @@ -125,6 +125,8 @@ class TextTreeStructure { : OS(OS), ShowColors(ShowColors) {} }; +/// Dumps additional data associated with a single AST node; recursive traversal +/// of AST nodes is handled via 'children()' or manually in ASTNodeTraverser.h. class TextNodeDumper : public TextTreeStructure, public comments::ConstCommentVisitor; def Friend : DeclNode; def FriendTemplate : DeclNode; def StaticAssert : DeclNode; +def ExpansionStmt : DeclNode, DeclContext; def Block : DeclNode, DeclContext; def OutlinedFunction : DeclNode, DeclContext; def Captured : DeclNode, DeclContext; diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index e5e071f43fa75..b4dee76722893 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -16,6 +16,10 @@ let CategoryName = "Parse Issue" in { defm enum_fixed_underlying_type : CXX11Compat< "enumeration types with a fixed underlying type are", /*ext_warn=*/false>; + +// C++26 compatibility with C++23. +defm expansion_statements : CXX26Compat< + "expansion statements are">; } def err_asm_qualifier_ignored : Error< @@ -416,9 +420,10 @@ def warn_cxx98_compat_for_range : Warning< "range-based for loop is incompatible with C++98">, InGroup, DefaultIgnore; def err_for_range_identifier : Error< - "range-based for loop requires type for loop variable">; + "%select{range-based for loop|expansion statement}0 requires " + "type for %select{loop|expansion}0 variable">; def err_for_range_expected_decl : Error< - "for range declaration must declare a variable">; + "%select{for range|expansion statement}0 declaration must declare a variable">; def err_argument_required_after_attribute : Error< "argument required after attribute">; def err_missing_param : Error<"expected parameter declarator">; @@ -445,6 +450,8 @@ def err_unspecified_size_with_static : Error< "'static' may not be used without an array size">; def err_expected_parentheses_around_typename : Error< "expected parentheses around type name in %0 expression">; +def err_expansion_stmt_requires_range : Error< + "expansion statement must be a range-based for loop">; def err_expected_case_before_expression: Error< "expected 'case' keyword before expression">; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 4e369be0bbb92..bc172e8a62bb4 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -165,6 +165,14 @@ def err_ice_too_large : Error< def err_expr_not_string_literal : Error<"expression is not a string literal">; def note_constexpr_assert_failed : Note< "assertion failed during evaluation of constant expression">; +def err_expansion_size_expr_not_ice : Error< + "expansion size is not a constant expression">; +def err_expansion_size_negative : Error< + "expansion size must not be negative (was %0)">; +def err_expansion_too_big : Error< + "expansion size %0 exceeds maximum configured size %1">; +def note_use_fexpansion_limit : Note< + "use -fexpansion-limit=N to adjust this limit">; // Semantic analysis of constant literals. def ext_predef_outside_function : Warning< @@ -2902,8 +2910,8 @@ def note_which_delegates_to : Note<"which delegates to">; def err_for_range_decl_must_be_var : Error< "for range declaration must declare a variable">; def err_for_range_storage_class : Error< - "loop variable %0 may not be declared %select{'extern'|'static'|" - "'__private_extern__'|'auto'|'register'|'constexpr'|'thread_local'}1">; + "%select{loop|expansion}0 variable %1 may not be declared %select{'extern'|'static'|" + "'__private_extern__'|'auto'|'register'|'constexpr'|'thread_local'}2">; def err_type_defined_in_for_range : Error< "types may not be defined in a for range declaration">; def err_for_range_deduction_failure : Error< @@ -3678,6 +3686,17 @@ def err_conflicting_codeseg_attribute : Error< def warn_duplicate_codeseg_attribute : Warning< "duplicate code segment specifiers">, InGroup
; +def err_expansion_stmt_invalid_init : Error< + "cannot expand expression of type %0">; +def err_expansion_stmt_lambda : Error< + "cannot expand lambda closure type">; +def err_expansion_stmt_case : Error< + "%select{'case'|'default'}0 belongs to 'switch' outside enclosing expansion statement">; +def note_enclosing_switch_statement_here : Note< + "switch statement is here">; +def err_expansion_stmt_label : Error< + "labels are not allowed in expansion statements">; + def err_attribute_patchable_function_entry_invalid_section : Error<"section argument to 'patchable_function_entry' attribute is not " "valid for this target: %0">; @@ -5807,6 +5826,8 @@ def note_template_nsdmi_here : Note< "in instantiation of default member initializer %q0 requested here">; def note_template_type_alias_instantiation_here : Note< "in instantiation of template type alias %0 requested here">; +def note_expansion_stmt_instantiation_here : Note< + "in instantiation of expansion statement requested here">; def note_template_exception_spec_instantiation_here : Note< "in instantiation of exception specification for %0 requested here">; def note_template_requirement_instantiation_here : Note< diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index 8d6b8a14740ce..d69a1712e4f0e 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -376,6 +376,7 @@ LANGOPT(ConstexprCallDepth, 32, 512, Benign, "maximum constexpr call depth") LANGOPT(ConstexprStepLimit, 32, 1048576, Benign, "maximum constexpr evaluation steps") +LANGOPT(MaxTemplateForExpansions, 32, 256, Benign, "maximum template for expansions") LANGOPT(EnableNewConstInterp, 1, 0, Benign, "enable the experimental new constant interpreter") LANGOPT(BracketDepth, 32, 256, Benign, diff --git a/clang/include/clang/Basic/StmtNodes.td b/clang/include/clang/Basic/StmtNodes.td index bf3686bb372d5..0141b84cac583 100644 --- a/clang/include/clang/Basic/StmtNodes.td +++ b/clang/include/clang/Basic/StmtNodes.td @@ -58,6 +58,14 @@ def CXXForRangeStmt : StmtNode; def CoroutineBodyStmt : StmtNode; def CoreturnStmt : StmtNode; +// C++ expansion statements (P1306) +def CXXExpansionStmt : StmtNode; +def CXXEnumeratingExpansionStmt : StmtNode; +def CXXIteratingExpansionStmt : StmtNode; +def CXXDestructuringExpansionStmt : StmtNode; +def CXXDependentExpansionStmt : StmtNode; +def CXXExpansionInstantiationStmt : StmtNode; // *Not* derived from CXXExpansionStmt! + // Expressions def Expr : StmtNode; def PredefinedExpr : StmtNode; @@ -177,6 +185,11 @@ def CoyieldExpr : StmtNode; def ConceptSpecializationExpr : StmtNode; def RequiresExpr : StmtNode; +// C++26 Expansion statement support expressions +def CXXExpansionInitListExpr : StmtNode; +def CXXExpansionInitListSelectExpr : StmtNode; +def CXXDestructuringExpansionSelectExpr : StmtNode; + // Obj-C Expressions. def ObjCStringLiteral : StmtNode; def ObjCBoxedExpr : StmtNode; diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 8784c9d7d206d..73446dde1593e 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -2049,6 +2049,10 @@ def fconstexpr_steps_EQ : Joined<["-"], "fconstexpr-steps=">, Group, Visibility<[ClangOption, CC1Option]>, HelpText<"Set the maximum number of steps in constexpr function evaluation (0 = no limit)">, MarshallingInfoInt, "1048576">; +def fexpansion_limit_EQ : Joined<["-"], "fexpansion-limit=">, Group, + Visibility<[ClangOption, CC1Option]>, + HelpText<"Set the maximum number of times a single expansion statement may be expanded (0 = no limit)">, + MarshallingInfoInt, "256">; def fexperimental_new_constant_interpreter : Flag<["-"], "fexperimental-new-constant-interpreter">, Group, HelpText<"Enable the experimental new constant interpreter">, Visibility<[ClangOption, CC1Option]>, diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index dad8efd0f017f..631035ed81e15 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -1700,11 +1700,13 @@ class Parser : public CodeCompletionHandler { } /// Information on a C++0x for-range-initializer found while parsing a - /// declaration which turns out to be a for-range-declaration. + /// declaration which turns out to be a for-range-declaration. Also used + /// for C++26's expansion statements. struct ForRangeInit { SourceLocation ColonLoc; ExprResult RangeExpr; SmallVector LifetimeExtendTemps; + bool ExpansionStmt = false; bool ParsedForRangeDecl() { return !ColonLoc.isInvalid(); } }; struct ForRangeInfo : ForRangeInit { @@ -4186,7 +4188,8 @@ class Parser : public CodeCompletionHandler { bool ParseExpressionList(SmallVectorImpl &Exprs, llvm::function_ref ExpressionStarts = llvm::function_ref(), - bool FailImmediatelyOnInvalidExpr = false); + bool FailImmediatelyOnInvalidExpr = false, + bool StopAtRBraceAfterComma = false); /// ParseSimpleExpressionList - A simple comma-separated list of expressions, /// used for misc language extensions. @@ -5250,6 +5253,16 @@ class Parser : public CodeCompletionHandler { /// ExprResult ParseBraceInitializer(); + /// ParseExpansionInitList - Called when the initializer of an expansion + /// statement starts with an open brace. + /// + /// \verbatim + /// expansion-init-list: [C++26 [stmt.expand]] + /// '{' expression-list ','[opt] '}' + /// '{' '}' + /// \endverbatim + ExprResult ParseExpansionInitList(); + struct DesignatorCompletionInfo { SmallVectorImpl &InitExprs; QualType PreferredBaseType; @@ -7456,8 +7469,12 @@ class Parser : public CodeCompletionHandler { /// [C++0x] expression /// [C++0x] braced-init-list [TODO] /// \endverbatim - StmtResult ParseForStatement(SourceLocation *TrailingElseLoc, - LabelDecl *PrecedingLabel); + StmtResult + ParseForStatement(SourceLocation *TrailingElseLoc, LabelDecl *PrecedingLabel, + ExpansionStmtDecl *ExpansionStmtDeclaration = nullptr); + + void ParseForRangeInitializerAfterColon(ForRangeInit &FRI, + ParsingDeclSpec *VarDeclSpec); /// ParseGotoStatement /// \verbatim @@ -7504,6 +7521,22 @@ class Parser : public CodeCompletionHandler { StmtResult ParseBreakOrContinueStatement(bool IsContinue); + /// ParseExpansionStatement - Parse a C++26 expansion + /// statement ('template for'). + /// + /// \verbatim + /// expansion-statement: + /// 'template' 'for' '(' init-statement[opt] + /// for-range-declaration ':' expansion-initializer ')' + /// compound-statement + /// + /// expansion-initializer: + /// expression + /// expansion-init-list + /// \endverbatim + StmtResult ParseExpansionStatement(SourceLocation *TrailingElseLoc, + LabelDecl *PrecedingLabel); + StmtResult ParsePragmaLoopHint(StmtVector &Stmts, ParsedStmtContext StmtCtx, SourceLocation *TrailingElseLoc, ParsedAttributes &Attrs, diff --git a/clang/include/clang/Sema/ScopeInfo.h b/clang/include/clang/Sema/ScopeInfo.h index 4f4d38c961140..2a410bd2eab91 100644 --- a/clang/include/clang/Sema/ScopeInfo.h +++ b/clang/include/clang/Sema/ScopeInfo.h @@ -202,7 +202,11 @@ class FunctionScopeInfo { public: /// A SwitchStmt, along with a flag indicating if its list of case statements /// is incomplete (because we dropped an invalid one while parsing). - using SwitchInfo = llvm::PointerIntPair; + struct SwitchInfo : llvm::PointerIntPair { + DeclContext *EnclosingDC; + SwitchInfo(SwitchStmt *Switch, DeclContext *DC) + : PointerIntPair(Switch, false), EnclosingDC(DC) {} + }; /// SwitchStack - This is the current set of active switch statements in the /// block. diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 52904c72d1cfc..368a32c93776f 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -893,6 +893,7 @@ class Sema final : public SemaBase { // 33. Types (SemaType.cpp) // 34. FixIt Helpers (SemaFixItUtils.cpp) // 35. Function Effects (SemaFunctionEffects.cpp) + // 36. C++ Expansion Statements (SemaExpand.cpp) /// \name Semantic Analysis /// Implementations are in Sema.cpp @@ -4097,7 +4098,7 @@ class Sema final : public SemaBase { /// complete. void ActOnInitializerError(Decl *Dcl); - void ActOnCXXForRangeDecl(Decl *D); + void ActOnCXXForRangeDecl(Decl *D, bool InExpansionStmt); StmtResult ActOnCXXForRangeIdentifier(Scope *S, SourceLocation IdentLoc, IdentifierInfo *Ident, ParsedAttributes &Attrs); @@ -9502,7 +9503,8 @@ class Sema final : public SemaBase { /// of an __label__ label name, otherwise it is a normal label definition /// or use. LabelDecl *LookupOrCreateLabel(IdentifierInfo *II, SourceLocation IdentLoc, - SourceLocation GnuLabelLoc = SourceLocation()); + SourceLocation GnuLabelLoc = SourceLocation(), + bool ForLabelStmt = false); /// Perform a name lookup for a label with the specified name; this does not /// create a new label if the lookup fails. @@ -11049,6 +11051,37 @@ class Sema final : public SemaBase { BuildForRangeKind Kind, ArrayRef LifetimeExtendTemps = {}); + /// Set the type of a for-range declaration whose for-range or expansion + /// initialiser is dependent. + void ActOnDependentForRangeInitializer(VarDecl *LoopVar, + BuildForRangeKind BFRK); + + /// Holds the 'begin' and 'end' variables of a range-based for loop or + /// expansion statement; begin-expr and end-expr are also provided; the + /// latter are used in some diagnostics. + struct ForRangeBeginEndInfo { + VarDecl *BeginVar = nullptr; + VarDecl *EndVar = nullptr; + Expr *BeginExpr = nullptr; + Expr *EndExpr = nullptr; + bool isValid() const { return BeginVar != nullptr && EndVar != nullptr; } + }; + + /// Determine begin-expr and end-expr and build variable declarations for + /// them as per [stmt.ranged]. + ForRangeBeginEndInfo BuildCXXForRangeBeginEndVars( + Scope *S, VarDecl *RangeVar, SourceLocation ColonLoc, + SourceLocation CoawaitLoc, + ArrayRef LifetimeExtendTemps, + BuildForRangeKind Kind, bool ForExpansionStmt, + StmtResult *RebuildResult = nullptr, + llvm::function_ref RebuildWithDereference = {}); + + /// Build the range variable of a range-based for loop or iterating + /// expansion statement and return its DeclStmt. + StmtResult BuildCXXForRangeRangeVar(Scope *S, Expr *Range, + bool ForExpansionStmt); + /// FinishCXXForRangeStmt - Attach the body to a C++0x for-range statement. /// This is a separate step from ActOnCXXForRangeStmt because analysis of the /// body cannot be performed until after the type of the range variable is @@ -11190,6 +11223,9 @@ class Sema final : public SemaBase { SourceLocation Loc, unsigned NumParams); + void ApplyForRangeOrExpansionStatementLifetimeExtension( + VarDecl *RangeVar, ArrayRef Temporaries); + private: /// Check whether the given statement can have musttail applied to it, /// issuing a diagnostic and returning false if not. @@ -13146,6 +13182,9 @@ class Sema final : public SemaBase { /// We are performing partial ordering for template template parameters. PartialOrderingTTP, + + /// We are instantiating an expansion statement. + ExpansionStmtInstantiation, } Kind; /// Was the enclosing context a non-instantiation SFINAE context? @@ -13357,6 +13396,12 @@ class Sema final : public SemaBase { sema::TemplateDeductionInfo &DeductionInfo, SourceRange InstantiationRange = SourceRange()); + /// \brief Note that we are substituting the body of an expansion statement. + InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation, + CXXExpansionStmt *ExpansionStmt, + ArrayRef TArgs, + SourceRange InstantiationRange); + /// \brief Note that we are checking the satisfaction of the constraint /// expression inside of a nested requirement. InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation, @@ -15606,6 +15651,63 @@ class Sema final : public SemaBase { void performFunctionEffectAnalysis(TranslationUnitDecl *TU); ///@} + + // + // + // ------------------------------------------------------------------------- + // + // + + /// \name Expansion Statements + /// Implementations are in SemaExpand.cpp + ///@{ +public: + ExpansionStmtDecl *ActOnExpansionStmtDecl(unsigned TemplateDepth, + SourceLocation TemplateKWLoc); + + ExpansionStmtDecl *BuildExpansionStmtDecl(DeclContext *Ctx, + SourceLocation TemplateKWLoc, + NonTypeTemplateParmDecl *NTTP); + + ExprResult ActOnCXXExpansionInitList(MultiExprArg SubExprs, + SourceLocation LBraceLoc, + SourceLocation RBraceLoc); + + StmtResult ActOnCXXExpansionStmt( + ExpansionStmtDecl *ESD, Stmt *Init, Stmt *ExpansionVarStmt, + Expr *ExpansionInitializer, SourceLocation ForLoc, + SourceLocation LParenLoc, SourceLocation ColonLoc, + SourceLocation RParenLoc, + ArrayRef LifetimeExtendTemps); + + StmtResult FinishCXXExpansionStmt(Stmt *Expansion, Stmt *Body); + + ExprResult BuildCXXExpansionInitializer(ExpansionStmtDecl *ESD, + Expr *ExpansionInitializer); + + StmtResult BuildCXXEnumeratingExpansionStmt(Decl *ESD, Stmt *Init, + Stmt *ExpansionVar, + SourceLocation ForLoc, + SourceLocation LParenLoc, + SourceLocation ColonLoc, + SourceLocation RParenLoc); + + ExprResult + BuildCXXExpansionInitListSelectExpr(CXXExpansionInitListExpr *Range, + Expr *Idx); + + ExprResult BuildCXXDestructuringExpansionSelectExpr(DecompositionDecl *DD, + Expr *Idx); + + StmtResult BuildNonEnumeratingCXXExpansionStmt( + ExpansionStmtDecl *ESD, Stmt *Init, DeclStmt *ExpansionVarStmt, + Expr *ExpansionInitializer, SourceLocation ForLoc, + SourceLocation LParenLoc, SourceLocation ColonLoc, + SourceLocation RParenLoc, + ArrayRef LifetimeExtendTemps = {}); + + std::optional ComputeExpansionSize(CXXExpansionStmt *Expansion); + ///@} }; DeductionFailureInfo diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index 5d09d5536e5ab..faa1fba71311b 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,21 @@ enum StmtCode { STMT_CXX_FOR_RANGE, + /// A CXXEnumeratedExpansionStmt. + STMT_CXX_ENUMERATING_EXPANSION, + + /// A CXXIteratingExpansionStmt. + STMT_CXX_ITERATING_EXPANSION, + + /// A CXXDestructuringExpansionStmt. + STMT_CXX_DESTRUCTURING_EXPANSION, + + /// A CXXDependentExpansionStmt, + STMT_CXX_DEPENDENT_EXPANSION, + + /// A CXXExpansionInstantiationStmt. + STMT_CXX_EXPANSION_INSTANTIATION, + /// A CXXOperatorCallExpr record. EXPR_CXX_OPERATOR_CALL, @@ -1914,16 +1932,19 @@ enum StmtCode { EXPR_TYPE_TRAIT, // TypeTraitExpr EXPR_ARRAY_TYPE_TRAIT, // ArrayTypeTraitIntExpr - EXPR_PACK_EXPANSION, // PackExpansionExpr - EXPR_PACK_INDEXING, // PackIndexingExpr - EXPR_SIZEOF_PACK, // SizeOfPackExpr - EXPR_SUBST_NON_TYPE_TEMPLATE_PARM, // SubstNonTypeTemplateParmExpr - EXPR_SUBST_NON_TYPE_TEMPLATE_PARM_PACK, // SubstNonTypeTemplateParmPackExpr - EXPR_FUNCTION_PARM_PACK, // FunctionParmPackExpr - EXPR_MATERIALIZE_TEMPORARY, // MaterializeTemporaryExpr - EXPR_CXX_FOLD, // CXXFoldExpr - EXPR_CONCEPT_SPECIALIZATION, // ConceptSpecializationExpr - EXPR_REQUIRES, // RequiresExpr + EXPR_PACK_EXPANSION, // PackExpansionExpr + EXPR_PACK_INDEXING, // PackIndexingExpr + EXPR_SIZEOF_PACK, // SizeOfPackExpr + EXPR_SUBST_NON_TYPE_TEMPLATE_PARM, // SubstNonTypeTemplateParmExpr + EXPR_SUBST_NON_TYPE_TEMPLATE_PARM_PACK, // SubstNonTypeTemplateParmPackExpr + EXPR_FUNCTION_PARM_PACK, // FunctionParmPackExpr + EXPR_MATERIALIZE_TEMPORARY, // MaterializeTemporaryExpr + EXPR_CXX_FOLD, // CXXFoldExpr + EXPR_CONCEPT_SPECIALIZATION, // ConceptSpecializationExpr + EXPR_REQUIRES, // RequiresExpr + EXPR_CXX_EXPANSION_INIT_LIST, // CXXExpansionInitListExpr + EXPR_CXX_EXPANSION_INIT_LIST_SELECT, // CXXExpansionInitListSelectExpr + EXPR_CXX_DESTRUCTURING_EXPANSION_SELECT, // CXXDestructuringExpansionSelectExpr // CUDA EXPR_CUDA_KERNEL_CALL, // CUDAKernelCallExpr diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index bf51c3e42719c..0fd716b904a54 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 VisitExpansionStmtDecl(ExpansionStmtDecl *D); ExpectedDecl VisitTranslationUnitDecl(TranslationUnitDecl *D); ExpectedDecl VisitBindingDecl(BindingDecl *D); ExpectedDecl VisitNamespaceDecl(NamespaceDecl *D); @@ -608,6 +609,14 @@ namespace clang { ExpectedStmt VisitCXXCatchStmt(CXXCatchStmt *S); ExpectedStmt VisitCXXTryStmt(CXXTryStmt *S); ExpectedStmt VisitCXXForRangeStmt(CXXForRangeStmt *S); + ExpectedStmt + VisitCXXEnumeratingExpansionStmt(CXXEnumeratingExpansionStmt *S); + ExpectedStmt VisitCXXIteratingExpansionStmt(CXXIteratingExpansionStmt *S); + ExpectedStmt + VisitCXXDestructuringExpansionStmt(CXXDestructuringExpansionStmt *S); + ExpectedStmt VisitCXXDependentExpansionStmt(CXXDependentExpansionStmt *S); + ExpectedStmt + VisitCXXExpansionInstantiationStmt(CXXExpansionInstantiationStmt *S); // FIXME: MSDependentExistsStmt ExpectedStmt VisitObjCForCollectionStmt(ObjCForCollectionStmt *S); ExpectedStmt VisitObjCAtCatchStmt(ObjCAtCatchStmt *S); @@ -696,6 +705,11 @@ namespace clang { ExpectedStmt VisitCXXFoldExpr(CXXFoldExpr *E); ExpectedStmt VisitRequiresExpr(RequiresExpr* E); ExpectedStmt VisitConceptSpecializationExpr(ConceptSpecializationExpr* E); + ExpectedStmt VisitCXXExpansionInitListExpr(CXXExpansionInitListExpr *E); + ExpectedStmt + VisitCXXExpansionInitListSelectExpr(CXXExpansionInitListSelectExpr *E); + ExpectedStmt VisitCXXDestructuringExpansionSelectExpr( + CXXDestructuringExpansionSelectExpr *E); // Helper for chaining together multiple imports. If an error is detected, // subsequent imports will return default constructed nodes, so that failure @@ -2828,6 +2842,33 @@ ExpectedDecl ASTNodeImporter::VisitStaticAssertDecl(StaticAssertDecl *D) { return ToD; } +ExpectedDecl ASTNodeImporter::VisitExpansionStmtDecl(ExpansionStmtDecl *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 ToTemplateParams = importChecked(Err, D->getTemplateParameters()); + auto ToInstantiations = importChecked(Err, D->getInstantiations()); + if (Err) + return std::move(Err); + + ExpansionStmtDecl *ToD; + if (GetImportedOrCreateDecl(ToD, D, Importer.getToContext(), DC, ToLocation, + ToTemplateParams)) + 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; @@ -7421,6 +7462,101 @@ ExpectedStmt ASTNodeImporter::VisitCXXForRangeStmt(CXXForRangeStmt *S) { ToBody, ToForLoc, ToCoawaitLoc, ToColonLoc, ToRParenLoc); } +ExpectedStmt ASTNodeImporter::VisitCXXEnumeratingExpansionStmt( + CXXEnumeratingExpansionStmt *S) { + Error Err = Error::success(); + auto ToESD = importChecked(Err, S->getDecl()); + auto ToInit = importChecked(Err, S->getInit()); + auto ToExpansionVar = importChecked(Err, S->getExpansionVarStmt()); + auto ToForLoc = importChecked(Err, S->getForLoc()); + auto ToLParenLoc = importChecked(Err, S->getLParenLoc()); + auto ToColonLoc = importChecked(Err, S->getColonLoc()); + auto ToRParenLoc = importChecked(Err, S->getRParenLoc()); + if (Err) + return std::move(Err); + + return new (Importer.getToContext()) + CXXEnumeratingExpansionStmt(ToESD, ToInit, ToExpansionVar, ToForLoc, + ToLParenLoc, ToColonLoc, ToRParenLoc); +} +ExpectedStmt +ASTNodeImporter::VisitCXXIteratingExpansionStmt(CXXIteratingExpansionStmt *S) { + Error Err = Error::success(); + auto ToESD = importChecked(Err, S->getDecl()); + auto ToInit = importChecked(Err, S->getInit()); + auto ToExpansionVar = importChecked(Err, S->getExpansionVarStmt()); + auto ToRange = importChecked(Err, S->getRangeVarStmt()); + auto ToBegin = importChecked(Err, S->getBeginVarStmt()); + auto ToEnd = importChecked(Err, S->getEndVarStmt()); + auto ToForLoc = importChecked(Err, S->getForLoc()); + auto ToLParenLoc = importChecked(Err, S->getLParenLoc()); + auto ToColonLoc = importChecked(Err, S->getColonLoc()); + auto ToRParenLoc = importChecked(Err, S->getRParenLoc()); + if (Err) + return std::move(Err); + + return new (Importer.getToContext()) CXXIteratingExpansionStmt( + ToESD, ToInit, ToExpansionVar, ToRange, ToBegin, ToEnd, ToForLoc, + ToLParenLoc, ToColonLoc, ToRParenLoc); +} +ExpectedStmt ASTNodeImporter::VisitCXXDestructuringExpansionStmt( + CXXDestructuringExpansionStmt *S) { + Error Err = Error::success(); + auto ToESD = importChecked(Err, S->getDecl()); + auto ToInit = importChecked(Err, S->getInit()); + auto ToExpansionVar = importChecked(Err, S->getExpansionVarStmt()); + auto ToDecompositionDeclStmt = + importChecked(Err, S->getDecompositionDeclStmt()); + auto ToForLoc = importChecked(Err, S->getForLoc()); + auto ToLParenLoc = importChecked(Err, S->getLParenLoc()); + auto ToColonLoc = importChecked(Err, S->getColonLoc()); + auto ToRParenLoc = importChecked(Err, S->getRParenLoc()); + if (Err) + return std::move(Err); + + return new (Importer.getToContext()) CXXDestructuringExpansionStmt( + ToESD, ToInit, ToExpansionVar, ToDecompositionDeclStmt, ToForLoc, + ToLParenLoc, ToColonLoc, ToRParenLoc); +} +ExpectedStmt +ASTNodeImporter::VisitCXXDependentExpansionStmt(CXXDependentExpansionStmt *S) { + Error Err = Error::success(); + auto ToESD = importChecked(Err, S->getDecl()); + auto ToInit = importChecked(Err, S->getInit()); + auto ToExpansionVar = importChecked(Err, S->getExpansionVarStmt()); + auto ToExpansionInitializer = + importChecked(Err, S->getExpansionInitializer()); + auto ToForLoc = importChecked(Err, S->getForLoc()); + auto ToLParenLoc = importChecked(Err, S->getLParenLoc()); + auto ToColonLoc = importChecked(Err, S->getColonLoc()); + auto ToRParenLoc = importChecked(Err, S->getRParenLoc()); + if (Err) + return std::move(Err); + + return new (Importer.getToContext()) CXXDependentExpansionStmt( + ToESD, ToInit, ToExpansionVar, ToExpansionInitializer, ToForLoc, + ToLParenLoc, ToColonLoc, ToRParenLoc); +} +ExpectedStmt ASTNodeImporter::VisitCXXExpansionInstantiationStmt( + CXXExpansionInstantiationStmt *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 CXXExpansionInstantiationStmt::Create( + Importer.getToContext(), ToBeginLoc, ToEndLoc, ToInstantiations, + ToSharedStmts, S->shouldApplyLifetimeExtensionToSharedStmts()); +} + ExpectedStmt ASTNodeImporter::VisitObjCForCollectionStmt(ObjCForCollectionStmt *S) { Error Err = Error::success(); @@ -9273,6 +9409,46 @@ ASTNodeImporter::VisitConceptSpecializationExpr(ConceptSpecializationExpr *E) { const_cast(CSD), &Satisfaction); } +ExpectedStmt +ASTNodeImporter::VisitCXXExpansionInitListExpr(CXXExpansionInitListExpr *E) { + Error Err = Error::success(); + SmallVector ToExprs; + auto ToLBraceLoc = importChecked(Err, E->getLBraceLoc()); + auto ToRBraceLoc = importChecked(Err, E->getRBraceLoc()); + for (Expr *FromInst : E->getExprs()) + ToExprs.push_back(importChecked(Err, FromInst)); + + if (Err) + return std::move(Err); + + return CXXExpansionInitListExpr::Create(Importer.getToContext(), ToExprs, + ToLBraceLoc, ToRBraceLoc); +} + +ExpectedStmt ASTNodeImporter::VisitCXXExpansionInitListSelectExpr( + CXXExpansionInitListSelectExpr *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()) + CXXExpansionInitListSelectExpr(Importer.getToContext(), ToRange, ToIndex); +} + +ExpectedStmt ASTNodeImporter::VisitCXXDestructuringExpansionSelectExpr( + CXXDestructuringExpansionSelectExpr *E) { + Error Err = Error::success(); + auto ToDecompositionDecl = importChecked(Err, E->getDecompositionDecl()); + auto ToIndex = importChecked(Err, E->getIndexExpr()); + if (Err) + return std::move(Err); + + return new (Importer.getToContext()) CXXDestructuringExpansionSelectExpr( + Importer.getToContext(), ToDecompositionDecl, ToIndex); +} + Error ASTNodeImporter::ImportOverriddenMethods(CXXMethodDecl *ToMethod, CXXMethodDecl *FromMethod) { Error ImportErrors = Error::success(); diff --git a/clang/lib/AST/ComputeDependence.cpp b/clang/lib/AST/ComputeDependence.cpp index e0cf0deb12bd2..b5f2a22bdf54e 100644 --- a/clang/lib/AST/ComputeDependence.cpp +++ b/clang/lib/AST/ComputeDependence.cpp @@ -959,3 +959,10 @@ ExprDependence clang::computeDependence(OpenACCAsteriskSizeExpr *E) { // way. return ExprDependence::None; } + +ExprDependence clang::computeDependence(CXXExpansionInitListExpr *ILE) { + auto D = ExprDependence::None; + for (Expr *E : ILE->getExprs()) + D |= E->getDependence(); + return D; +} diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp index 30c6d3ed91f1e..07e0e2437b7be 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 ExpansionStmt: // 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::ExpansionStmt: // 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..6bc06918dbe98 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 VisitExpansionStmtDecl(const ExpansionStmtDecl *D); void VisitOpenACCDeclareDecl(OpenACCDeclareDecl *D); void VisitOpenACCRoutineDecl(OpenACCRoutineDecl *D); @@ -1329,6 +1330,11 @@ void DeclPrinter::VisitClassTemplatePartialSpecializationDecl( VisitCXXRecordDecl(D); } +void DeclPrinter::VisitExpansionStmtDecl(const ExpansionStmtDecl *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..1cd88efb5c2a0 100644 --- a/clang/lib/AST/DeclTemplate.cpp +++ b/clang/lib/AST/DeclTemplate.cpp @@ -1717,6 +1717,10 @@ clang::getReplacedTemplateParameter(Decl *D, unsigned Index) { return getReplacedTemplateParameter( cast(D)->getTemplateSpecializationInfo()->getTemplate(), Index); + case Decl::Kind::ExpansionStmt: + return { + cast(D)->getTemplateParameters()->getParam(Index), + {}}; default: llvm_unreachable("Unhandled templated declaration kind"); } @@ -1788,3 +1792,22 @@ const Decl &clang::adjustDeclToTemplate(const Decl &D) { // FIXME: Adjust alias templates? return D; } + +ExpansionStmtDecl::ExpansionStmtDecl(DeclContext *DC, SourceLocation Loc, + TemplateParameterList *TParams) + : Decl(ExpansionStmt, DC, Loc), DeclContext(ExpansionStmt), + TParams(TParams) {} + +ExpansionStmtDecl *ExpansionStmtDecl::Create(ASTContext &C, DeclContext *DC, + SourceLocation Loc, + TemplateParameterList *TParams) { + return new (C, DC) ExpansionStmtDecl(DC, Loc, TParams); +} +ExpansionStmtDecl *ExpansionStmtDecl::CreateDeserialized(ASTContext &C, + GlobalDeclID ID) { + return new (C, ID) ExpansionStmtDecl(nullptr, SourceLocation(), nullptr); +} + +SourceRange ExpansionStmtDecl::getSourceRange() const { + return Expansion ? Expansion->getSourceRange() : SourceRange(); +} diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index 340bb4b2ed6a3..c61660c90513f 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -3688,6 +3688,9 @@ bool Expr::HasSideEffects(const ASTContext &Ctx, case FunctionParmPackExprClass: case RecoveryExprClass: case CXXFoldExprClass: + case CXXExpansionInitListSelectExprClass: + case CXXExpansionInitListExprClass: + case CXXDestructuringExpansionSelectExprClass: // 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..7ba49f74c1f7d 100644 --- a/clang/lib/AST/ExprCXX.cpp +++ b/clang/lib/AST/ExprCXX.cpp @@ -2020,3 +2020,61 @@ CXXFoldExpr::CXXFoldExpr(QualType T, UnresolvedLookupExpr *Callee, SubExprs[SubExpr::RHS] = RHS; setDependence(computeDependence(this)); } + +CXXExpansionInitListExpr::CXXExpansionInitListExpr(EmptyShell ES, + unsigned NumExprs) + : Expr(CXXExpansionInitListExprClass, ES), NumExprs(NumExprs) {} + +CXXExpansionInitListExpr::CXXExpansionInitListExpr(ArrayRef Exprs, + SourceLocation LBraceLoc, + SourceLocation RBraceLoc) + : Expr(CXXExpansionInitListExprClass, QualType(), VK_PRValue, OK_Ordinary), + NumExprs(static_cast(Exprs.size())), LBraceLoc(LBraceLoc), + RBraceLoc(RBraceLoc) { + llvm::uninitialized_copy(Exprs, getTrailingObjects()); + setDependence(computeDependence(this)); +} + +CXXExpansionInitListExpr * +CXXExpansionInitListExpr::Create(const ASTContext &C, ArrayRef Exprs, + SourceLocation LBraceLoc, + SourceLocation RBraceLoc) { + void *Mem = C.Allocate(totalSizeToAlloc(Exprs.size())); + return new (Mem) CXXExpansionInitListExpr(Exprs, LBraceLoc, RBraceLoc); +} + +CXXExpansionInitListExpr * +CXXExpansionInitListExpr::CreateEmpty(const ASTContext &C, EmptyShell Empty, + unsigned NumExprs) { + void *Mem = C.Allocate(totalSizeToAlloc(NumExprs)); + return new (Mem) CXXExpansionInitListExpr(Empty, NumExprs); +} + +CXXExpansionInitListSelectExpr::CXXExpansionInitListSelectExpr(EmptyShell Empty) + : Expr(CXXExpansionInitListSelectExprClass, Empty) {} + +CXXExpansionInitListSelectExpr::CXXExpansionInitListSelectExpr( + const ASTContext &C, CXXExpansionInitListExpr *Range, Expr *Idx) + : Expr(CXXExpansionInitListSelectExprClass, C.DependentTy, VK_PRValue, + OK_Ordinary) { + setDependence(ExprDependence::TypeValueInstantiation); + SubExprs[RANGE] = Range; + SubExprs[INDEX] = Idx; +} + +bool CXXExpansionInitListExpr::containsPackExpansion() const { + return llvm::any_of(getExprs(), + [](const Expr *E) { return isa(E); }); +} + +CXXDestructuringExpansionSelectExpr::CXXDestructuringExpansionSelectExpr( + EmptyShell Empty) + : Expr(CXXDestructuringExpansionSelectExprClass, Empty) {} + +CXXDestructuringExpansionSelectExpr::CXXDestructuringExpansionSelectExpr( + const ASTContext &C, DecompositionDecl *Decomposition, Expr *Index) + : Expr(CXXDestructuringExpansionSelectExprClass, C.DependentTy, VK_PRValue, + OK_Ordinary), + Decomposition(Decomposition), Index(Index) { + setDependence(ExprDependence::TypeValueInstantiation); +} diff --git a/clang/lib/AST/ExprClassification.cpp b/clang/lib/AST/ExprClassification.cpp index aeacd0dc765ef..5521cdf9d04c9 100644 --- a/clang/lib/AST/ExprClassification.cpp +++ b/clang/lib/AST/ExprClassification.cpp @@ -216,6 +216,9 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { case Expr::SourceLocExprClass: case Expr::ConceptSpecializationExprClass: case Expr::RequiresExprClass: + case Expr::CXXExpansionInitListExprClass: + case Expr::CXXExpansionInitListSelectExprClass: + case Expr::CXXDestructuringExpansionSelectExprClass: return Cl::CL_PRValue; case Expr::EmbedExprClass: diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index d0404b957ab03..df5ffd11b4ca4 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -5665,6 +5665,12 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info, const VarDecl *VD = dyn_cast_or_null(D); if (VD && !CheckLocalVariableDeclaration(Info, VD)) return ESR_Failed; + + if (const auto *ESD = dyn_cast(D)) { + assert(ESD->getInstantiations() && "not expanded?"); + return EvaluateStmt(Result, Info, ESD->getInstantiations(), Case); + } + // Each declaration initialization is its own full-expression. FullExpressionRAII Scope(Info); if (!EvaluateDecl(Info, D, /*EvaluateConditionDecl=*/true) && @@ -5937,6 +5943,40 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info, return Scope.destroy() ? ESR_Succeeded : ESR_Failed; } + case Stmt::CXXExpansionInstantiationStmtClass: { + BlockScopeRAII Scope(Info); + const auto *Expansion = cast(S); + for (const Stmt *Shared : Expansion->getSharedStmts()) { + EvalStmtResult ESR = EvaluateStmt(Result, Info, Shared); + if (ESR != ESR_Succeeded) { + if (ESR != ESR_Failed && !Scope.destroy()) + return ESR_Failed; + return ESR; + } + } + + // No need to push an extra scope for these since they're already + // CompoundStmts. + EvalStmtResult ESR = ESR_Succeeded; + for (const Stmt *Instantiation : Expansion->getInstantiations()) { + ESR = EvaluateStmt(Result, Info, Instantiation); + if (ESR == ESR_Failed || + ShouldPropagateBreakContinue(Info, Expansion, &Scope, ESR)) + return ESR; + if (ESR != ESR_Continue) { + // Succeeded here actually means we encountered a 'break'. + assert(ESR == ESR_Succeeded || ESR == ESR_Returned); + break; + } + } + + // Map Continue back to Succeeded if we fell off the end of the loop. + if (ESR == ESR_Continue) + ESR = ESR_Succeeded; + + return Scope.destroy() ? ESR : ESR_Failed; + } + case Stmt::SwitchStmtClass: return EvaluateSwitch(Result, Info, cast(S)); @@ -19287,6 +19327,9 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) { case Expr::SYCLUniqueStableNameExprClass: case Expr::CXXParenListInitExprClass: case Expr::HLSLOutArgExprClass: + case Expr::CXXExpansionInitListExprClass: + case Expr::CXXExpansionInitListSelectExprClass: + case Expr::CXXDestructuringExpansionSelectExprClass: 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..2223347ba3b10 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,9 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity, case Expr::CXXInheritedCtorInitExprClass: case Expr::CXXParenListInitExprClass: case Expr::PackIndexingExprClass: + case Expr::CXXExpansionInitListSelectExprClass: + case Expr::CXXExpansionInitListExprClass: + case Expr::CXXDestructuringExpansionSelectExprClass: llvm_unreachable("unexpected statement kind"); case Expr::ConstantExprClass: diff --git a/clang/lib/AST/StmtCXX.cpp b/clang/lib/AST/StmtCXX.cpp index 6a69fe75136f3..8bd1acd5ae383 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,150 @@ CoroutineBodyStmt::CoroutineBodyStmt(CoroutineBodyStmt::CtorArgs const &Args) Args.ReturnStmtOnAllocFailure; llvm::copy(Args.ParamMoves, const_cast(getParamMoves().data())); } + +CXXExpansionStmt::CXXExpansionStmt(StmtClass SC, EmptyShell Empty) + : Stmt(SC, Empty) {} + +CXXExpansionStmt::CXXExpansionStmt(StmtClass SC, ExpansionStmtDecl *ESD, + Stmt *Init, DeclStmt *ExpansionVar, + SourceLocation ForLoc, + SourceLocation LParenLoc, + SourceLocation ColonLoc, + SourceLocation RParenLoc) + : Stmt(SC), ParentDecl(ESD), ForLoc(ForLoc), LParenLoc(LParenLoc), + ColonLoc(ColonLoc), RParenLoc(RParenLoc) { + setInit(Init); + setExpansionVarStmt(ExpansionVar); + setBody(nullptr); +} + +CXXEnumeratingExpansionStmt::CXXEnumeratingExpansionStmt(EmptyShell Empty) + : CXXExpansionStmt(CXXEnumeratingExpansionStmtClass, Empty) {} + +CXXEnumeratingExpansionStmt::CXXEnumeratingExpansionStmt( + ExpansionStmtDecl *ESD, Stmt *Init, DeclStmt *ExpansionVar, + SourceLocation ForLoc, SourceLocation LParenLoc, SourceLocation ColonLoc, + SourceLocation RParenLoc) + : CXXExpansionStmt(CXXEnumeratingExpansionStmtClass, ESD, Init, + ExpansionVar, ForLoc, LParenLoc, ColonLoc, RParenLoc) {} + +SourceLocation CXXExpansionStmt::getBeginLoc() const { + return ParentDecl->getLocation(); +} + +VarDecl *CXXExpansionStmt::getExpansionVariable() { + Decl *LV = cast(getExpansionVarStmt())->getSingleDecl(); + assert(LV && "No expansion variable in CXXExpansionStmt"); + return cast(LV); +} + +bool CXXExpansionStmt::hasDependentSize() const { + if (isa(this)) + return cast( + getExpansionVariable()->getInit()) + ->getRangeExpr() + ->containsPackExpansion(); + + if (auto *Iterating = dyn_cast(this)) { + const Expr *Begin = Iterating->getBeginVar()->getInit(); + const Expr *End = Iterating->getBeginVar()->getInit(); + return Begin->isTypeDependent() || Begin->isValueDependent() || + End->isTypeDependent() || End->isValueDependent(); + } + + if (isa(this)) + return false; + + if (isa(this)) + return true; + + llvm_unreachable("Invalid expansion statement class"); +} + +CXXIteratingExpansionStmt::CXXIteratingExpansionStmt(EmptyShell Empty) + : CXXExpansionStmt(CXXIteratingExpansionStmtClass, Empty) {} + +CXXIteratingExpansionStmt::CXXIteratingExpansionStmt( + ExpansionStmtDecl *ESD, Stmt *Init, DeclStmt *ExpansionVar, DeclStmt *Range, + DeclStmt *Begin, DeclStmt *End, SourceLocation ForLoc, + SourceLocation LParenLoc, SourceLocation ColonLoc, SourceLocation RParenLoc) + : CXXExpansionStmt(CXXIteratingExpansionStmtClass, ESD, Init, ExpansionVar, + ForLoc, LParenLoc, ColonLoc, RParenLoc) { + setRangeVarStmt(Range); + setBeginVarStmt(Begin); + setEndVarStmt(End); +} + +CXXDestructuringExpansionStmt::CXXDestructuringExpansionStmt(EmptyShell Empty) + : CXXExpansionStmt(CXXDestructuringExpansionStmtClass, Empty) {} + +CXXDestructuringExpansionStmt::CXXDestructuringExpansionStmt( + ExpansionStmtDecl *ESD, Stmt *Init, DeclStmt *ExpansionVar, + Stmt *DecompositionDeclStmt, SourceLocation ForLoc, + SourceLocation LParenLoc, SourceLocation ColonLoc, SourceLocation RParenLoc) + : CXXExpansionStmt(CXXDestructuringExpansionStmtClass, ESD, Init, + ExpansionVar, ForLoc, LParenLoc, ColonLoc, RParenLoc) { + setDecompositionDeclStmt(DecompositionDeclStmt); +} + +DecompositionDecl *CXXDestructuringExpansionStmt::getDecompositionDecl() { + return cast( + cast(getDecompositionDeclStmt())->getSingleDecl()); +} + +CXXDependentExpansionStmt::CXXDependentExpansionStmt(EmptyShell Empty) + : CXXExpansionStmt(CXXDependentExpansionStmtClass, Empty) {} + +CXXDependentExpansionStmt::CXXDependentExpansionStmt( + ExpansionStmtDecl *ESD, Stmt *Init, DeclStmt *ExpansionVar, + Expr *ExpansionInitializer, SourceLocation ForLoc, SourceLocation LParenLoc, + SourceLocation ColonLoc, SourceLocation RParenLoc) + : CXXExpansionStmt(CXXDependentExpansionStmtClass, ESD, Init, ExpansionVar, + ForLoc, LParenLoc, ColonLoc, RParenLoc) { + setExpansionInitializer(ExpansionInitializer); +} + +CXXExpansionInstantiationStmt::CXXExpansionInstantiationStmt( + EmptyShell Empty, unsigned NumInstantiations, unsigned NumSharedStmts) + : Stmt(CXXExpansionInstantiationStmtClass, Empty), + NumInstantiations(NumInstantiations), NumSharedStmts(NumSharedStmts) { + assert(NumSharedStmts <= 4 && "might have to allocate more bits for this"); +} + +CXXExpansionInstantiationStmt::CXXExpansionInstantiationStmt( + SourceLocation BeginLoc, SourceLocation EndLoc, + ArrayRef Instantiations, ArrayRef SharedStmts, + bool ShouldApplyLifetimeExtensionToSharedStmts) + : Stmt(CXXExpansionInstantiationStmtClass), 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); +} + +CXXExpansionInstantiationStmt *CXXExpansionInstantiationStmt::Create( + ASTContext &C, SourceLocation BeginLoc, SourceLocation EndLoc, + ArrayRef Instantiations, ArrayRef SharedStmts, + bool ShouldApplyLifetimeExtensionToSharedStmts) { + void *Mem = C.Allocate( + totalSizeToAlloc(Instantiations.size() + SharedStmts.size()), + alignof(CXXExpansionInstantiationStmt)); + return new (Mem) CXXExpansionInstantiationStmt( + BeginLoc, EndLoc, Instantiations, SharedStmts, + ShouldApplyLifetimeExtensionToSharedStmts); +} + +CXXExpansionInstantiationStmt * +CXXExpansionInstantiationStmt::CreateEmpty(ASTContext &C, EmptyShell Empty, + unsigned NumInstantiations, + unsigned NumSharedStmts) { + void *Mem = + C.Allocate(totalSizeToAlloc(NumInstantiations + NumSharedStmts), + alignof(CXXExpansionInstantiationStmt)); + return new (Mem) + CXXExpansionInstantiationStmt(Empty, NumInstantiations, NumSharedStmts); +} diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index ff8ca01ec5477..ce4455fd14479 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -160,6 +160,8 @@ namespace { } void VisitCXXNamedCastExpr(CXXNamedCastExpr *Node); + void VisitCXXExpansionStmt(CXXExpansionStmt *Node, + Expr *Initializer = nullptr); #define ABSTRACT_STMT(CLASS) #define STMT(CLASS, PARENT) \ @@ -263,7 +265,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 +450,63 @@ void StmtPrinter::VisitCXXForRangeStmt(CXXForRangeStmt *Node) { PrintControlledStmt(Node->getBody()); } +void StmtPrinter::VisitCXXExpansionStmt(CXXExpansionStmt *Node, + Expr *Initializer) { + OS << "template for ("; + if (Node->getInit()) + PrintInitStmt(Node->getInit(), 14); + PrintingPolicy SubPolicy(Policy); + SubPolicy.SuppressInitializers = true; + Node->getExpansionVariable()->print(OS, SubPolicy, IndentLevel); + OS << " : "; + PrintExpr(Initializer ? Initializer + : Node->getExpansionVariable()->getInit()); + OS << ")"; + PrintControlledStmt(Node->getBody()); +} + +void StmtPrinter::VisitCXXEnumeratingExpansionStmt( + CXXEnumeratingExpansionStmt *Node) { + VisitCXXExpansionStmt(Node); +} + +void StmtPrinter::VisitCXXIteratingExpansionStmt( + CXXIteratingExpansionStmt *Node) { + VisitCXXExpansionStmt(Node, Node->getRangeVar()->getInit()); +} + +void StmtPrinter::VisitCXXDestructuringExpansionStmt( + CXXDestructuringExpansionStmt *Node) { + VisitCXXExpansionStmt(Node); +} + +void StmtPrinter::VisitCXXDependentExpansionStmt( + CXXDependentExpansionStmt *Node) { + VisitCXXExpansionStmt(Node, Node->getExpansionInitializer()); +} + +void StmtPrinter::VisitCXXExpansionInstantiationStmt( + CXXExpansionInstantiationStmt *) { + llvm_unreachable("should never be printed"); +} + +void StmtPrinter::VisitCXXExpansionInitListExpr( + CXXExpansionInitListExpr *Node) { + OS << "{ "; + llvm::interleaveComma(Node->getExprs(), OS, [&](Expr *E) { PrintExpr(E); }); + OS << " }"; +} + +void StmtPrinter::VisitCXXExpansionInitListSelectExpr( + CXXExpansionInitListSelectExpr *Node) { + PrintExpr(Node->getRangeExpr()); +} + +void StmtPrinter::VisitCXXDestructuringExpansionSelectExpr( + CXXDestructuringExpansionSelectExpr *Node) { + PrintExpr(Node->getDecompositionDecl()->getInit()); +} + void StmtPrinter::VisitMSDependentExistsStmt(MSDependentExistsStmt *Node) { Indent(); if (Node->isIfExists()) diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp index 05b64ccda0d01..dc4553882c843 100644 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -364,6 +364,36 @@ void StmtProfiler::VisitCXXForRangeStmt(const CXXForRangeStmt *S) { VisitStmt(S); } +void StmtProfiler::VisitCXXExpansionStmt(const CXXExpansionStmt *S) { + VisitStmt(S); +} + +void StmtProfiler::VisitCXXEnumeratingExpansionStmt( + const CXXEnumeratingExpansionStmt *S) { + VisitCXXExpansionStmt(S); +} + +void StmtProfiler::VisitCXXIteratingExpansionStmt( + const CXXIteratingExpansionStmt *S) { + VisitCXXExpansionStmt(S); +} + +void StmtProfiler::VisitCXXDestructuringExpansionStmt( + const CXXDestructuringExpansionStmt *S) { + VisitCXXExpansionStmt(S); +} + +void StmtProfiler::VisitCXXDependentExpansionStmt( + const CXXDependentExpansionStmt *S) { + VisitCXXExpansionStmt(S); +} + +void StmtProfiler::VisitCXXExpansionInstantiationStmt( + const CXXExpansionInstantiationStmt *S) { + VisitStmt(S); + ID.AddBoolean(S->shouldApplyLifetimeExtensionToSharedStmts()); +} + void StmtProfiler::VisitMSDependentExistsStmt(const MSDependentExistsStmt *S) { VisitStmt(S); ID.AddBoolean(S->isIfExists()); @@ -2392,6 +2422,22 @@ void StmtProfiler::VisitSourceLocExpr(const SourceLocExpr *E) { void StmtProfiler::VisitEmbedExpr(const EmbedExpr *E) { VisitExpr(E); } +void StmtProfiler::VisitCXXExpansionInitListExpr( + const CXXExpansionInitListExpr *E) { + VisitExpr(E); +} + +void StmtProfiler::VisitCXXExpansionInitListSelectExpr( + const CXXExpansionInitListSelectExpr *E) { + VisitExpr(E); +} + +void StmtProfiler::VisitCXXDestructuringExpansionSelectExpr( + const CXXDestructuringExpansionSelectExpr *E) { + VisitExpr(E); + VisitDecl(E->getDecompositionDecl()); +} + 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..b4833e2338893 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,12 @@ void clang::TextNodeDumper::VisitCoreturnStmt(const CoreturnStmt *Node) { OS << " implicit"; } +void TextNodeDumper::VisitCXXExpansionInstantiationStmt( + const CXXExpansionInstantiationStmt *Node) { + if (Node->shouldApplyLifetimeExtensionToSharedStmts()) + OS << " applies_lifetime_extension"; +} + void TextNodeDumper::VisitConstantExpr(const ConstantExpr *Node) { if (Node->hasAPValueResult()) AddChild("value", @@ -1826,6 +1836,17 @@ void TextNodeDumper::VisitCXXDependentScopeMemberExpr( OS << " " << (Node->isArrow() ? "->" : ".") << Node->getMember(); } +void TextNodeDumper::VisitCXXExpansionInitListExpr( + const CXXExpansionInitListExpr *Node) { + if (Node->containsPackExpansion()) + OS << " contains_pack"; +} + +void TextNodeDumper::VisitCXXDestructuringExpansionSelectExpr( + const CXXDestructuringExpansionSelectExpr *Node) { + dumpDeclRef(Node->getDecompositionDecl()); +} + void TextNodeDumper::VisitObjCMessageExpr(const ObjCMessageExpr *Node) { OS << " selector="; Node->getSelector().print(OS); diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp index 8b1cd83af2396..a8f1000640f3a 100644 --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -143,6 +143,13 @@ void CodeGenFunction::EmitDecl(const Decl &D, bool EvaluateConditionDecl) { // None of these decls require codegen support. return; + case Decl::ExpansionStmt: { + const auto *ESD = cast(&D); + assert(ESD->getInstantiations() && "expansion statement not expanded?"); + EmitStmt(ESD->getInstantiations()); + return; + } + 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 fdc1a11f6c55c..2892ef8a6e1ee 100644 --- a/clang/lib/CodeGen/CGStmt.cpp +++ b/clang/lib/CodeGen/CGStmt.cpp @@ -204,6 +204,14 @@ void CodeGenFunction::EmitStmt(const Stmt *S, ArrayRef Attrs) { case Stmt::CXXForRangeStmtClass: EmitCXXForRangeStmt(cast(*S), Attrs); break; + case Stmt::CXXEnumeratingExpansionStmtClass: + case Stmt::CXXIteratingExpansionStmtClass: + case Stmt::CXXDestructuringExpansionStmtClass: + case Stmt::CXXDependentExpansionStmtClass: + llvm_unreachable("unexpanded expansion statements should not be emitted"); + case Stmt::CXXExpansionInstantiationStmtClass: + EmitCXXExpansionInstantiationStmt(cast(*S)); + break; case Stmt::SEHTryStmtClass: EmitSEHTryStmt(cast(*S)); break; @@ -1559,6 +1567,44 @@ CodeGenFunction::EmitCXXForRangeStmt(const CXXForRangeStmt &S, } } +void CodeGenFunction::EmitCXXExpansionInstantiationStmt( + const CXXExpansionInstantiationStmt &S) { + // FIXME: For reasons beyond my understanding, two scopes are required to emit + // the destructors of lifetime-extended temporaries in the right place, but + // only in some templates. There are some other issues with lifetime-extended + // temporaries currently (https://github.com/llvm/llvm-project/issues/165182); + // perhaps resolving those will allow us to remove the second scope here + // because there really ought to be a better way of doing this. + LexicalScope Scope(*this, S.getSourceRange()); + LexicalScope Scope2(*this, S.getSourceRange()); + + for (const Stmt *DS : S.getSharedStmts()) + EmitStmt(DS); + + if (S.getInstantiations().empty() || !HaveInsertPoint()) + return; + + JumpDest ExpandExit = getJumpDestInCurrentScope("expand.end"); + JumpDest ContinueDest; + for (auto [N, Inst] : enumerate(S.getInstantiations())) { + if (!HaveInsertPoint()) { + EmitBlock(ExpandExit.getBlock(), true); + return; + } + + if (N == S.getInstantiations().size() - 1) + ContinueDest = ExpandExit; + else + ContinueDest = getJumpDestInCurrentScope("expand.next"); + + LexicalScope ExpansionScope(*this, S.getSourceRange()); + BreakContinueStack.push_back(BreakContinue(S, ExpandExit, ContinueDest)); + EmitStmt(Inst); + BreakContinueStack.pop_back(); + EmitBlock(ContinueDest.getBlock(), true); + } +} + void CodeGenFunction::EmitReturnOfRValue(RValue RV, QualType Ty) { if (RV.isScalar()) { Builder.CreateStore(RV.getScalarVal(), ReturnValue); diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index 8c4c1c8c2dc95..1616b6e60709b 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -3690,6 +3690,9 @@ class CodeGenFunction : public CodeGenTypeCache { void EmitCXXForRangeStmt(const CXXForRangeStmt &S, ArrayRef Attrs = {}); + void + EmitCXXExpansionInstantiationStmt(const CXXExpansionInstantiationStmt &S); + /// Controls insertion of cancellation exit blocks in worksharing constructs. class OMPCancelStackRAII { CodeGenFunction &CGF; diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 79edc561c551f..6421a36850ae9 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -6346,6 +6346,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, Args.AddLastArg(CmdArgs, options::OPT_foperator_arrow_depth_EQ); Args.AddLastArg(CmdArgs, options::OPT_fconstexpr_depth_EQ); Args.AddLastArg(CmdArgs, options::OPT_fconstexpr_steps_EQ); + Args.AddLastArg(CmdArgs, options::OPT_fexpansion_limit_EQ); Args.AddLastArg(CmdArgs, options::OPT_fexperimental_library); diff --git a/clang/lib/Frontend/FrontendActions.cpp b/clang/lib/Frontend/FrontendActions.cpp index d7d56b8166350..2c3950c5b0b52 100644 --- a/clang/lib/Frontend/FrontendActions.cpp +++ b/clang/lib/Frontend/FrontendActions.cpp @@ -476,6 +476,8 @@ class DefaultTemplateInstCallback : public TemplateInstantiationCallback { return "TypeAliasTemplateInstantiation"; case CodeSynthesisContext::PartialOrderingTTP: return "PartialOrderingTTP"; + case CodeSynthesisContext::ExpansionStmtInstantiation: + return "ExpansionStmtInstantiation"; } return ""; } diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index e4b158e4a6248..e700e96ef8e53 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -2304,43 +2304,18 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS, // Handle the Objective-C for-in loop variable similarly, although we // don't need to parse the container in advance. if (FRI && (Tok.is(tok::colon) || isTokIdentifier_in())) { - bool IsForRangeLoop = false; + bool IsForRangeLoopOrExpansionStmt = false; if (TryConsumeToken(tok::colon, FRI->ColonLoc)) { - IsForRangeLoop = true; - EnterExpressionEvaluationContext ForRangeInitContext( - Actions, Sema::ExpressionEvaluationContext::PotentiallyEvaluated, - /*LambdaContextDecl=*/nullptr, - Sema::ExpressionEvaluationContextRecord::EK_Other, - getLangOpts().CPlusPlus23); - - // P2718R0 - Lifetime extension in range-based for loops. - if (getLangOpts().CPlusPlus23) { - auto &LastRecord = Actions.currentEvaluationContext(); - LastRecord.InLifetimeExtendingContext = true; - LastRecord.RebuildDefaultArgOrDefaultInit = true; - } - - if (getLangOpts().OpenMP) + IsForRangeLoopOrExpansionStmt = true; + if (getLangOpts().OpenMP && !FRI->ExpansionStmt) Actions.OpenMP().startOpenMPCXXRangeFor(); - if (Tok.is(tok::l_brace)) - FRI->RangeExpr = ParseBraceInitializer(); - else - FRI->RangeExpr = ParseExpression(); - - // Before c++23, ForRangeLifetimeExtendTemps should be empty. - assert( - getLangOpts().CPlusPlus23 || - Actions.ExprEvalContexts.back().ForRangeLifetimeExtendTemps.empty()); - // Move the collected materialized temporaries into ForRangeInit before - // ForRangeInitContext exit. - FRI->LifetimeExtendTemps = std::move( - Actions.ExprEvalContexts.back().ForRangeLifetimeExtendTemps); + ParseForRangeInitializerAfterColon(*FRI, &DS); } Decl *ThisDecl = Actions.ActOnDeclarator(getCurScope(), D); - if (IsForRangeLoop) { - Actions.ActOnCXXForRangeDecl(ThisDecl); + if (IsForRangeLoopOrExpansionStmt) { + Actions.ActOnCXXForRangeDecl(ThisDecl, FRI->ExpansionStmt); } else { // Obj-C for loop if (auto *VD = dyn_cast_or_null(ThisDecl)) diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp index 3515343202de1..902afcaeed987 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -3166,7 +3166,8 @@ void Parser::injectEmbedTokens() { bool Parser::ParseExpressionList(SmallVectorImpl &Exprs, llvm::function_ref ExpressionStarts, - bool FailImmediatelyOnInvalidExpr) { + bool FailImmediatelyOnInvalidExpr, + bool StopAtRBraceAfterComma) { bool SawError = false; while (true) { if (ExpressionStarts) @@ -3195,7 +3196,11 @@ bool Parser::ParseExpressionList(SmallVectorImpl &Exprs, SawError = true; if (FailImmediatelyOnInvalidExpr) break; - SkipUntil(tok::comma, tok::r_paren, StopAtSemi | StopBeforeMatch); + + if (StopAtRBraceAfterComma) + SkipUntil(tok::comma, tok::r_brace, StopAtSemi | StopBeforeMatch); + else + SkipUntil(tok::comma, tok::r_paren, StopAtSemi | StopBeforeMatch); } else { Exprs.push_back(Expr.get()); } @@ -3205,6 +3210,10 @@ bool Parser::ParseExpressionList(SmallVectorImpl &Exprs, // Move to the next argument, remember where the comma was. Token Comma = Tok; ConsumeToken(); + + if (StopAtRBraceAfterComma && Tok.is(tok::r_brace)) + break; + checkPotentialAngleBracketDelimiter(Comma); } return SawError; diff --git a/clang/lib/Parse/ParseInit.cpp b/clang/lib/Parse/ParseInit.cpp index a3be3744a9327..89b8fb80565a3 100644 --- a/clang/lib/Parse/ParseInit.cpp +++ b/clang/lib/Parse/ParseInit.cpp @@ -516,6 +516,26 @@ ExprResult Parser::ParseBraceInitializer() { return ExprError(); // an error occurred. } +ExprResult Parser::ParseExpansionInitList() { + BalancedDelimiterTracker T(*this, tok::l_brace); + T.consumeOpen(); + + ExprVector InitExprs; + + // CWG 3061: Accept a trailing comma here. + if (!Tok.is(tok::r_brace) && + ParseExpressionList(InitExprs, /*ExpressionStarts=*/{}, + /*FailImmediatelyOnInvalidExpr=*/false, + /*StopAtRBraceAfterComma=*/true)) { + T.consumeClose(); + return ExprError(); + } + + T.consumeClose(); + return Actions.ActOnCXXExpansionInitList(InitExprs, T.getOpenLocation(), + T.getCloseLocation()); +} + bool Parser::ParseMicrosoftIfExistsBraceInitializer(ExprVector &InitExprs, bool &InitExprsOk) { bool trailingComma = false; diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp index 92038985f9163..fcc375da7a332 100644 --- a/clang/lib/Parse/ParseStmt.cpp +++ b/clang/lib/Parse/ParseStmt.cpp @@ -260,6 +260,9 @@ StmtResult Parser::ParseStatementOrDeclarationAfterAttributes( } case tok::kw_template: { + if (NextToken().is(tok::kw_for)) + return ParseExpansionStatement(TrailingElseLoc, PrecedingLabel); + SourceLocation DeclEnd; ParseTemplateDeclarationOrSpecialization(DeclaratorContext::Block, DeclEnd, getAccessSpecifierIfPresent()); @@ -701,8 +704,9 @@ StmtResult Parser::ParseLabeledStatement(ParsedAttributes &Attrs, // identifier ':' statement SourceLocation ColonLoc = ConsumeToken(); - LabelDecl *LD = Actions.LookupOrCreateLabel(IdentTok.getIdentifierInfo(), - IdentTok.getLocation()); + LabelDecl *LD = Actions.LookupOrCreateLabel( + IdentTok.getIdentifierInfo(), IdentTok.getLocation(), /*GnuLabelLoc=*/{}, + /*ForLabelStmt=*/true); // Read label attributes, if present. StmtResult SubStmt; @@ -746,6 +750,12 @@ StmtResult Parser::ParseLabeledStatement(ParsedAttributes &Attrs, DiagnoseLabelFollowedByDecl(*this, SubStmt.get()); + // If a label cannot appear here, just return the underlying statement. + if (!LD) { + Attrs.clear(); + return SubStmt.get(); + } + Actions.ProcessDeclAttributeList(Actions.CurScope, LD, Attrs); Attrs.clear(); @@ -1890,8 +1900,55 @@ bool Parser::isForRangeIdentifier() { return false; } -StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc, - LabelDecl *PrecedingLabel) { +void Parser::ParseForRangeInitializerAfterColon(ForRangeInit &FRI, + ParsingDeclSpec *VarDeclSpec) { + // Use an immediate function context if this is the initializer for a + // constexpr variable in an expansion statement. + auto Ctx = Sema::ExpressionEvaluationContext::PotentiallyEvaluated; + if (FRI.ExpansionStmt && VarDeclSpec && VarDeclSpec->hasConstexprSpecifier()) + Ctx = Sema::ExpressionEvaluationContext::ImmediateFunctionContext; + + EnterExpressionEvaluationContext InitContext( + Actions, Ctx, + /*LambdaContextDecl=*/nullptr, + Sema::ExpressionEvaluationContextRecord::EK_Other, + getLangOpts().CPlusPlus23); + + // P2718R0 - Lifetime extension in range-based for loops. + if (getLangOpts().CPlusPlus23) { + auto &LastRecord = Actions.currentEvaluationContext(); + LastRecord.InLifetimeExtendingContext = true; + LastRecord.RebuildDefaultArgOrDefaultInit = true; + } + + if (FRI.ExpansionStmt) { + Sema::ContextRAII CtxGuard( + Actions, Actions.CurContext->getEnclosingNonExpansionStatementContext(), + /*NewThis=*/false); + + FRI.RangeExpr = + Tok.is(tok::l_brace) ? ParseExpansionInitList() : ParseExpression(); + FRI.RangeExpr = Actions.MaybeCreateExprWithCleanups(FRI.RangeExpr); + } else if (Tok.is(tok::l_brace)) { + FRI.RangeExpr = ParseBraceInitializer(); + } else { + FRI.RangeExpr = ParseExpression(); + } + + // Before c++23, ForRangeLifetimeExtendTemps should be empty. + assert(getLangOpts().CPlusPlus23 || + Actions.ExprEvalContexts.back().ForRangeLifetimeExtendTemps.empty()); + + // Move the collected materialized temporaries into ForRangeInit before + // ForRangeInitContext exit. + FRI.LifetimeExtendTemps = + std::move(Actions.ExprEvalContexts.back().ForRangeLifetimeExtendTemps); +} + +StmtResult +Parser::ParseForStatement(SourceLocation *TrailingElseLoc, + LabelDecl *PrecedingLabel, + ExpansionStmtDecl *ExpansionStmtDeclaration) { assert(Tok.is(tok::kw_for) && "Not a for stmt!"); SourceLocation ForLoc = ConsumeToken(); // eat the 'for'. @@ -1926,6 +1983,8 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc, unsigned ScopeFlags = 0; if (C99orCXXorObjC) ScopeFlags = Scope::DeclScope | Scope::ControlScope; + if (ExpansionStmtDeclaration) + ScopeFlags |= Scope::TemplateParamScope; ParseScope ForScope(this, ScopeFlags); @@ -1940,6 +1999,7 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc, ExprResult Collection; ForRangeInfo ForRangeInfo; FullExprArg ThirdPart(Actions); + ForRangeInfo.ExpansionStmt = ExpansionStmtDeclaration != nullptr; if (Tok.is(tok::code_completion)) { cutOffParsing(); @@ -1970,18 +2030,17 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc, MaybeParseCXX11Attributes(attrs); ForRangeInfo.ColonLoc = ConsumeToken(); - if (Tok.is(tok::l_brace)) - ForRangeInfo.RangeExpr = ParseBraceInitializer(); - else - ForRangeInfo.RangeExpr = ParseExpression(); + ParseForRangeInitializerAfterColon(ForRangeInfo, /*VarDeclSpec=*/nullptr); Diag(Loc, diag::err_for_range_identifier) - << ((getLangOpts().CPlusPlus11 && !getLangOpts().CPlusPlus17) - ? FixItHint::CreateInsertion(Loc, "auto &&") - : FixItHint()); - - ForRangeInfo.LoopVar = - Actions.ActOnCXXForRangeIdentifier(getCurScope(), Loc, Name, attrs); + << ForRangeInfo.ExpansionStmt + << ((getLangOpts().CPlusPlus11 && !getLangOpts().CPlusPlus17) + ? FixItHint::CreateInsertion(Loc, "auto &&") + : FixItHint()); + + if (!ForRangeInfo.ExpansionStmt) + ForRangeInfo.LoopVar = + Actions.ActOnCXXForRangeIdentifier(getCurScope(), Loc, Name, attrs); } else if (isForInitDeclaration()) { // for (int X = 4; ParenBraceBracketBalancer BalancerRAIIObj(*this); @@ -2073,7 +2132,8 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc, // User tried to write the reasonable, but ill-formed, for-range-statement // for (expr : expr) { ... } Diag(Tok, diag::err_for_range_expected_decl) - << FirstPart.get()->getSourceRange(); + << (ExpansionStmtDeclaration != nullptr) + << FirstPart.get()->getSourceRange(); SkipUntil(tok::r_paren, StopBeforeMatch); SecondPart = Sema::ConditionError(); } else { @@ -2195,7 +2255,13 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc, StmtResult ForRangeStmt; StmtResult ForEachStmt; - if (ForRangeInfo.ParsedForRangeDecl()) { + if (ExpansionStmtDeclaration) { + ForRangeStmt = Actions.ActOnCXXExpansionStmt( + ExpansionStmtDeclaration, FirstPart.get(), ForRangeInfo.LoopVar.get(), + ForRangeInfo.RangeExpr.get(), ForLoc, T.getOpenLocation(), + ForRangeInfo.ColonLoc, T.getCloseLocation(), + ForRangeInfo.LifetimeExtendTemps); + } else if (ForRangeInfo.ParsedForRangeDecl()) { ForRangeStmt = Actions.ActOnCXXForRangeStmt( getCurScope(), ForLoc, CoawaitLoc, FirstPart.get(), ForRangeInfo.LoopVar.get(), ForRangeInfo.ColonLoc, @@ -2217,7 +2283,9 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc, // OpenACC Restricts a for-loop inside of certain construct/clause // combinations, so diagnose that here in OpenACC mode. SemaOpenACC::LoopInConstructRAII LCR{getActions().OpenACC()}; - if (ForRangeInfo.ParsedForRangeDecl()) + if (ExpansionStmtDeclaration) + ; // Nothing. + else if (ForRangeInfo.ParsedForRangeDecl()) getActions().OpenACC().ActOnRangeForStmtBegin(ForLoc, ForRangeStmt.get()); else getActions().OpenACC().ActOnForStmtBegin( @@ -2271,6 +2339,15 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc, return Actions.ObjC().FinishObjCForCollectionStmt(ForEachStmt.get(), Body.get()); + if (ExpansionStmtDeclaration) { + if (!ForRangeInfo.ParsedForRangeDecl()) { + Diag(ForLoc, diag::err_expansion_stmt_requires_range); + return StmtError(); + } + + return Actions.FinishCXXExpansionStmt(ForRangeStmt.get(), Body.get()); + } + if (ForRangeInfo.ParsedForRangeDecl()) return Actions.FinishCXXForRangeStmt(ForRangeStmt.get(), Body.get()); @@ -2630,6 +2707,38 @@ StmtResult Parser::ParseCXXCatchBlock(bool FnCatch) { return Actions.ActOnCXXCatchBlock(CatchLoc, ExceptionDecl, Block.get()); } +StmtResult Parser::ParseExpansionStatement(SourceLocation *TrailingElseLoc, + LabelDecl *PrecedingLabel) { + assert(Tok.is(tok::kw_template) && NextToken().is(tok::kw_for)); + SourceLocation TemplateLoc = ConsumeToken(); + DiagCompat(TemplateLoc, diag_compat::expansion_statements); + + ExpansionStmtDecl *ExpansionDecl = + Actions.ActOnExpansionStmtDecl(TemplateParameterDepth, TemplateLoc); + + CXXExpansionStmt *Expansion; + { + Sema::ContextRAII CtxGuard(Actions, ExpansionDecl, /*NewThis=*/false); + TemplateParameterDepthRAII TParamDepthGuard(TemplateParameterDepth); + ++TParamDepthGuard; + + StmtResult SR = + ParseForStatement(TrailingElseLoc, PrecedingLabel, ExpansionDecl); + if (SR.isInvalid()) + return SR; + + Expansion = cast(SR.get()); + ExpansionDecl->setExpansionPattern(Expansion); + } + + DeclSpec DS(AttrFactory); + DeclGroupPtrTy DeclGroupPtr = + Actions.FinalizeDeclaratorGroup(getCurScope(), DS, {ExpansionDecl}); + + return Actions.ActOnDeclStmt(DeclGroupPtr, Expansion->getBeginLoc(), + Expansion->getEndLoc()); +} + void Parser::ParseMicrosoftIfExistsStatement(StmtVector &Stmts) { IfExistsCondition Result; if (ParseMicrosoftIfExistsCondition(Result)) diff --git a/clang/lib/Sema/CMakeLists.txt b/clang/lib/Sema/CMakeLists.txt index 0ebf56ecffe69..ef729e22c1dc8 100644 --- a/clang/lib/Sema/CMakeLists.txt +++ b/clang/lib/Sema/CMakeLists.txt @@ -53,6 +53,7 @@ add_clang_library(clangSema SemaDeclCXX.cpp SemaDeclObjC.cpp SemaExceptionSpec.cpp + SemaExpand.cpp SemaExpr.cpp SemaExprCXX.cpp SemaExprMember.cpp diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index 23bf7f217a01a..420c59ccffc00 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -1631,14 +1631,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 fc3aabf5741ca..338a026b46322 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -7430,7 +7430,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"); } @@ -14591,7 +14591,7 @@ void Sema::ActOnUninitializedDecl(Decl *RealDecl) { } } -void Sema::ActOnCXXForRangeDecl(Decl *D) { +void Sema::ActOnCXXForRangeDecl(Decl *D, bool InExpansionStmt) { // If there is no declaration, there was an error parsing it. Ignore it. if (!D) return; @@ -14640,7 +14640,7 @@ void Sema::ActOnCXXForRangeDecl(Decl *D) { if (Error != -1) { Diag(VD->getOuterLocStart(), diag::err_for_range_storage_class) - << VD << Error; + << InExpansionStmt << VD << Error; D->setInvalidDecl(); } } diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index d41ab126c426f..817b7fa988993 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -2027,6 +2027,9 @@ static bool CheckConstexprDeclStmt(Sema &SemaRef, const FunctionDecl *Dcl, // - using-enum-declaration continue; + case Decl::ExpansionStmt: + continue; + case Decl::Typedef: case Decl::TypeAlias: { // - typedef declarations and alias-declarations that do not define diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp index a0483c3027199..dda6123bd25fc 100644 --- a/clang/lib/Sema/SemaExceptionSpec.cpp +++ b/clang/lib/Sema/SemaExceptionSpec.cpp @@ -1288,6 +1288,9 @@ CanThrowResult Sema::canThrow(const Stmt *S) { case Expr::ConvertVectorExprClass: case Expr::VAArgExprClass: case Expr::CXXParenListInitExprClass: + case Expr::CXXExpansionInitListSelectExprClass: + case Expr::CXXExpansionInitListExprClass: + case Expr::CXXDestructuringExpansionSelectExprClass: return canSubStmtsThrow(*this, S); case Expr::CompoundLiteralExprClass: @@ -1348,6 +1351,7 @@ CanThrowResult Sema::canThrow(const Stmt *S) { case Expr::DependentScopeDeclRefExprClass: case Expr::CXXFoldExprClass: case Expr::RecoveryExprClass: + case Expr::CXXDependentExpansionStmtClass: return CT_Dependent; case Expr::AsTypeExprClass: @@ -1538,6 +1542,10 @@ CanThrowResult Sema::canThrow(const Stmt *S) { case Stmt::SEHTryStmtClass: case Stmt::SwitchStmtClass: case Stmt::WhileStmtClass: + case Stmt::CXXEnumeratingExpansionStmtClass: + case Stmt::CXXIteratingExpansionStmtClass: + case Stmt::CXXDestructuringExpansionStmtClass: + case Stmt::CXXExpansionInstantiationStmtClass: return canSubStmtsThrow(*this, S); case Stmt::DeclStmtClass: { diff --git a/clang/lib/Sema/SemaExpand.cpp b/clang/lib/Sema/SemaExpand.cpp new file mode 100644 index 0000000000000..a9022c4b31ee1 --- /dev/null +++ b/clang/lib/Sema/SemaExpand.cpp @@ -0,0 +1,578 @@ +//===-- SemaExpand.cpp - Semantic Analysis for Expansion Statements--------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements semantic analysis for C++26 expansion statements, +// aka 'template for'. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/DeclCXX.h" +#include "clang/AST/ExprCXX.h" +#include "clang/AST/StmtCXX.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Sema/EnterExpressionEvaluationContext.h" +#include "clang/Sema/Lookup.h" +#include "clang/Sema/Overload.h" +#include "clang/Sema/Sema.h" +#include "clang/Sema/Template.h" + +using namespace clang; +using namespace sema; + +namespace { +struct IterableExpansionStmtData { + enum class State { + NotIterable, + Error, + Ok, + }; + + DeclStmt *RangeDecl = nullptr; + DeclStmt *BeginDecl = nullptr; + DeclStmt *EndDecl = nullptr; + Expr *Initializer = nullptr; + State TheState = State::NotIterable; + + bool isIterable() const { return TheState == State::Ok; } + bool hasError() { return TheState == State::Error; } +}; +} // namespace + +static bool CheckExpansionSize(Sema &S, uint64_t NumInstantiations, + SourceLocation Loc) { + unsigned Max = S.LangOpts.MaxTemplateForExpansions; + if (Max != 0 && NumInstantiations > Max) { + S.Diag(Loc, diag::err_expansion_too_big) << NumInstantiations << Max; + S.Diag(Loc, diag::note_use_fexpansion_limit); + return true; + } + + return false; +} + +// Build a 'DeclRefExpr' designating the template parameter '__N'. +static DeclRefExpr *BuildIndexDRE(Sema &S, ExpansionStmtDecl *ESD) { + return S.BuildDeclRefExpr(ESD->getIndexTemplateParm(), + S.Context.getPointerDiffType(), VK_PRValue, + ESD->getBeginLoc()); +} + +static bool FinaliseExpansionVar(Sema &S, VarDecl *ExpansionVar, + ExprResult Initializer) { + if (Initializer.isInvalid()) { + S.ActOnInitializerError(ExpansionVar); + return true; + } + + S.AddInitializerToDecl(ExpansionVar, Initializer.get(), /*DirectInit=*/false); + return ExpansionVar->isInvalidDecl(); +} + +static IterableExpansionStmtData +TryBuildIterableExpansionStmtInitializer(Sema &S, Expr *ExpansionInitializer, + Expr *Index, SourceLocation ColonLoc, + bool VarIsConstexpr) { + IterableExpansionStmtData Data; + + // C++26 [stmt.expand]p3: An expression is expansion-iterable if it does not + // have array type [...] + QualType Ty = ExpansionInitializer->getType().getNonReferenceType(); + if (Ty->isArrayType()) + return Data; + + // Lookup member and ADL 'begin()'/'end()'. Only check if they exist; even if + // they're deleted, inaccessible, etc., this is still an iterating expansion + // statement, albeit an ill-formed one. + DeclarationNameInfo BeginName(&S.PP.getIdentifierTable().get("begin"), + ColonLoc); + DeclarationNameInfo EndName(&S.PP.getIdentifierTable().get("end"), ColonLoc); + + // Try member lookup first. + bool FoundBeginEnd = false; + if (auto *Record = Ty->getAsCXXRecordDecl()) { + LookupResult BeginLR(S, BeginName, Sema::LookupMemberName); + LookupResult EndLR(S, EndName, Sema::LookupMemberName); + FoundBeginEnd = S.LookupQualifiedName(BeginLR, Record) && + S.LookupQualifiedName(EndLR, Record); + } + + // Try ADL. + if (!FoundBeginEnd) { + OverloadCandidateSet Candidates(ColonLoc, OverloadCandidateSet::CSK_Normal); + + S.AddArgumentDependentLookupCandidates( + BeginName.getName(), ColonLoc, ExpansionInitializer, + /*ExplicitTemplateArgs=*/nullptr, Candidates); + + if (Candidates.empty()) + return Data; + + Candidates.clear(OverloadCandidateSet::CSK_Normal); + S.AddArgumentDependentLookupCandidates( + EndName.getName(), ColonLoc, ExpansionInitializer, + /*ExplicitTemplateArgs=*/nullptr, Candidates); + + if (Candidates.empty()) + return Data; + } + + auto Ctx = Sema::ExpressionEvaluationContext::PotentiallyEvaluated; + if (VarIsConstexpr) + Ctx = Sema::ExpressionEvaluationContext::ImmediateFunctionContext; + EnterExpressionEvaluationContext ExprEvalCtx(S, Ctx); + + // The declarations should be attached to the parent decl context. + Sema::ContextRAII CtxGuard( + S, S.CurContext->getEnclosingNonExpansionStatementContext(), + /*NewThis=*/false); + + // Ok, we know that this is supposed to be an iterable expansion statement; + // delegate to the for-range code to build the range/begin/end variables. + // + // Any failure at this point is a hard error. + Data.TheState = IterableExpansionStmtData::State::Error; + Scope *Scope = S.getCurScope(); + StmtResult Var = S.BuildCXXForRangeRangeVar(Scope, ExpansionInitializer, + /*ForExpansionStmt=*/true); + if (Var.isInvalid()) + return Data; + + auto *RangeVar = cast(Var.get()); + Sema::ForRangeBeginEndInfo Info = S.BuildCXXForRangeBeginEndVars( + Scope, cast(RangeVar->getSingleDecl()), ColonLoc, + /*CoawaitLoc=*/{}, + /*LifetimeExtendTemps=*/{}, Sema::BFRK_Build, /*ForExpansionStmt=*/true); + + if (!Info.isValid()) + return Data; + + StmtResult BeginStmt = S.ActOnDeclStmt( + S.ConvertDeclToDeclGroup(Info.BeginVar), ColonLoc, ColonLoc); + StmtResult EndStmt = S.ActOnDeclStmt(S.ConvertDeclToDeclGroup(Info.EndVar), + ColonLoc, ColonLoc); + if (BeginStmt.isInvalid() || EndStmt.isInvalid()) + return Data; + + // Build '*(begin + i)'. + DeclRefExpr *Begin = S.BuildDeclRefExpr( + Info.BeginVar, Info.BeginVar->getType().getNonReferenceType(), VK_LValue, + ColonLoc); + + ExprResult BeginPlusI = + S.ActOnBinOp(Scope, ColonLoc, tok::plus, Begin, Index); + if (BeginPlusI.isInvalid()) + return Data; + + ExprResult Deref = + S.ActOnUnaryOp(Scope, ColonLoc, tok::star, BeginPlusI.get()); + if (Deref.isInvalid()) + return Data; + + Deref = S.MaybeCreateExprWithCleanups(Deref.get()); + Data.BeginDecl = BeginStmt.getAs(); + Data.EndDecl = EndStmt.getAs(); + Data.RangeDecl = RangeVar; + Data.Initializer = Deref.get(); + Data.TheState = IterableExpansionStmtData::State::Ok; + return Data; +} + +static StmtResult BuildDestructuringExpansionStmtDecl( + Sema &S, Expr *ExpansionInitializer, SourceLocation ColonLoc, + bool VarIsConstexpr, + ArrayRef LifetimeExtendTemps) { + auto Ctx = Sema::ExpressionEvaluationContext::PotentiallyEvaluated; + if (VarIsConstexpr) + Ctx = Sema::ExpressionEvaluationContext::ImmediateFunctionContext; + EnterExpressionEvaluationContext ExprEvalCtx(S, Ctx); + + // The declarations should be attached to the parent decl context. + Sema::ContextRAII CtxGuard( + S, S.CurContext->getEnclosingNonExpansionStatementContext(), + /*NewThis=*/false); + + UnsignedOrNone Arity = + S.GetDecompositionElementCount(ExpansionInitializer->getType(), ColonLoc); + + if (!Arity) { + S.Diag(ExpansionInitializer->getBeginLoc(), + diag::err_expansion_stmt_invalid_init) + << ExpansionInitializer->getType() + << ExpansionInitializer->getSourceRange(); + return StmtError(); + } + + if (CheckExpansionSize(S, *Arity, ColonLoc)) + return StmtError(); + + QualType AutoRRef = S.Context.getAutoRRefDeductType(); + SmallVector Bindings; + for (unsigned I = 0; I < *Arity; ++I) + Bindings.push_back(BindingDecl::Create( + S.Context, S.CurContext, ColonLoc, + S.getPreprocessor().getIdentifierInfo("__u" + std::to_string(I)), + AutoRRef)); + + TypeSourceInfo *TSI = S.Context.getTrivialTypeSourceInfo(AutoRRef); + auto *DD = + DecompositionDecl::Create(S.Context, S.CurContext, ColonLoc, ColonLoc, + AutoRRef, TSI, SC_Auto, Bindings); + + if (VarIsConstexpr) + DD->setConstexpr(true); + + S.ApplyForRangeOrExpansionStatementLifetimeExtension(DD, LifetimeExtendTemps); + S.AddInitializerToDecl(DD, ExpansionInitializer, false); + return S.ActOnDeclStmt(S.ConvertDeclToDeclGroup(DD), ColonLoc, ColonLoc); +} + +ExpansionStmtDecl *Sema::ActOnExpansionStmtDecl(unsigned TemplateDepth, + SourceLocation TemplateKWLoc) { + // Create a template parameter '__N'. This will be used to denote the index + // of the element that we're instantiating. CWG 3044 requires this type to + // be 'ptrdiff_t' for iterating expansion statements, so use that in all + // cases. + IdentifierInfo *ParmName = &Context.Idents.get("__N"); + QualType ParmTy = Context.getPointerDiffType(); + TypeSourceInfo *ParmTI = + Context.getTrivialTypeSourceInfo(ParmTy, TemplateKWLoc); + + auto *TParam = NonTypeTemplateParmDecl::Create( + Context, Context.getTranslationUnitDecl(), TemplateKWLoc, TemplateKWLoc, + TemplateDepth, /*Position=*/0, ParmName, ParmTy, /*ParameterPack=*/false, + ParmTI); + + return BuildExpansionStmtDecl(CurContext, TemplateKWLoc, TParam); +} + +ExpansionStmtDecl *Sema::BuildExpansionStmtDecl(DeclContext *Ctx, + SourceLocation TemplateKWLoc, + NonTypeTemplateParmDecl *NTTP) { + auto *TParamList = TemplateParameterList::Create( + Context, TemplateKWLoc, TemplateKWLoc, {NTTP}, TemplateKWLoc, + /*RequiresClause=*/nullptr); + auto *Result = + ExpansionStmtDecl::Create(Context, Ctx, TemplateKWLoc, TParamList); + Ctx->addDecl(Result); + return Result; +} + +ExprResult Sema::ActOnCXXExpansionInitList(MultiExprArg SubExprs, + SourceLocation LBraceLoc, + SourceLocation RBraceLoc) { + return CXXExpansionInitListExpr::Create(Context, SubExprs, LBraceLoc, + RBraceLoc); +} + +StmtResult Sema::ActOnCXXExpansionStmt( + ExpansionStmtDecl *ESD, Stmt *Init, Stmt *ExpansionVarStmt, + Expr *ExpansionInitializer, SourceLocation ForLoc, SourceLocation LParenLoc, + SourceLocation ColonLoc, SourceLocation RParenLoc, + ArrayRef LifetimeExtendTemps) { + if (!ExpansionInitializer || !ExpansionVarStmt) + return StmtError(); + + assert(CurContext->isExpansionStmt()); + auto *DS = cast(ExpansionVarStmt); + if (!DS->isSingleDecl()) { + Diag(DS->getBeginLoc(), diag::err_type_defined_in_for_range); + return StmtError(); + } + + VarDecl *ExpansionVar = cast(DS->getSingleDecl()); + if (!ExpansionVar || ExpansionVar->isInvalidDecl() || + ExpansionInitializer->containsErrors()) + return StmtError(); + + // This is an enumerating expansion statement. + if (auto *ILE = dyn_cast(ExpansionInitializer)) { + ExprResult Initializer = + BuildCXXExpansionInitListSelectExpr(ILE, BuildIndexDRE(*this, ESD)); + if (FinaliseExpansionVar(*this, ExpansionVar, Initializer)) + return StmtError(); + + // Note that lifetime extension only applies to destructurable expansion + // statements, so we just ignore 'LifetimeExtendedTemps' entirely for other + // types of expansion statements (this is CWG 3043). + return BuildCXXEnumeratingExpansionStmt(ESD, Init, DS, ForLoc, LParenLoc, + ColonLoc, RParenLoc); + } + + if (ExpansionInitializer->hasPlaceholderType()) { + ExprResult R = CheckPlaceholderExpr(ExpansionInitializer); + if (R.isInvalid()) + return StmtError(); + ExpansionInitializer = R.get(); + } + + if (DiagnoseUnexpandedParameterPack(ExpansionInitializer)) + return StmtError(); + + // Reject lambdas early. + if (auto *RD = ExpansionInitializer->getType()->getAsCXXRecordDecl(); + RD && RD->isLambda()) { + Diag(ExpansionInitializer->getBeginLoc(), diag::err_expansion_stmt_lambda); + return StmtError(); + } + + return BuildNonEnumeratingCXXExpansionStmt( + ESD, Init, DS, ExpansionInitializer, ForLoc, LParenLoc, ColonLoc, + RParenLoc, LifetimeExtendTemps); +} + +StmtResult Sema::BuildCXXEnumeratingExpansionStmt(Decl *ESD, Stmt *Init, + Stmt *ExpansionVar, + SourceLocation ForLoc, + SourceLocation LParenLoc, + SourceLocation ColonLoc, + SourceLocation RParenLoc) { + return new (Context) CXXEnumeratingExpansionStmt( + cast(ESD), Init, cast(ExpansionVar), ForLoc, + LParenLoc, ColonLoc, RParenLoc); +} + +StmtResult Sema::BuildNonEnumeratingCXXExpansionStmt( + ExpansionStmtDecl *ESD, Stmt *Init, DeclStmt *ExpansionVarStmt, + Expr *ExpansionInitializer, SourceLocation ForLoc, SourceLocation LParenLoc, + SourceLocation ColonLoc, SourceLocation RParenLoc, + ArrayRef LifetimeExtendTemps) { + VarDecl *ExpansionVar = cast(ExpansionVarStmt->getSingleDecl()); + + if (ExpansionInitializer->isTypeDependent()) { + ActOnDependentForRangeInitializer(ExpansionVar, BFRK_Build); + return new (Context) CXXDependentExpansionStmt( + ESD, Init, ExpansionVarStmt, ExpansionInitializer, ForLoc, LParenLoc, + ColonLoc, RParenLoc); + } + + // Otherwise, if it can be an iterating expansion statement, it is one. + DeclRefExpr *Index = BuildIndexDRE(*this, ESD); + IterableExpansionStmtData Data = TryBuildIterableExpansionStmtInitializer( + *this, ExpansionInitializer, Index, ColonLoc, + ExpansionVar->isConstexpr()); + if (Data.hasError()) { + ActOnInitializerError(ExpansionVar); + return StmtError(); + } + + if (Data.isIterable()) { + if (FinaliseExpansionVar(*this, ExpansionVar, Data.Initializer)) + return StmtError(); + + return new (Context) CXXIteratingExpansionStmt( + ESD, Init, ExpansionVarStmt, Data.RangeDecl, Data.BeginDecl, + Data.EndDecl, ForLoc, LParenLoc, ColonLoc, RParenLoc); + } + + // If not, try destructuring. + StmtResult DecompDeclStmt = BuildDestructuringExpansionStmtDecl( + *this, ExpansionInitializer, ColonLoc, ExpansionVar->isConstexpr(), + LifetimeExtendTemps); + if (DecompDeclStmt.isInvalid()) { + ActOnInitializerError(ExpansionVar); + return StmtError(); + } + + auto *DS = DecompDeclStmt.getAs(); + auto *DD = cast(DS->getSingleDecl()); + if (DD->isInvalidDecl()) + return StmtError(); + + ExprResult Select = BuildCXXDestructuringExpansionSelectExpr(DD, Index); + if (Select.isInvalid()) { + ActOnInitializerError(ExpansionVar); + return StmtError(); + } + + if (FinaliseExpansionVar(*this, ExpansionVar, Select)) + return StmtError(); + + return new (Context) CXXDestructuringExpansionStmt( + ESD, Init, ExpansionVarStmt, DS, ForLoc, LParenLoc, ColonLoc, RParenLoc); +} + +StmtResult Sema::FinishCXXExpansionStmt(Stmt *Exp, Stmt *Body) { + if (!Exp || !Body) + return StmtError(); + + auto *Expansion = cast(Exp); + assert(!Expansion->getDecl()->getInstantiations() && + "should not rebuild expansion statement after instantiation"); + + Expansion->setBody(Body); + if (Expansion->hasDependentSize()) + return Expansion; + + // This can fail if this is an iterating expansion statement. + std::optional NumInstantiations = ComputeExpansionSize(Expansion); + if (!NumInstantiations) + return StmtError(); + + if (CheckExpansionSize(*this, *NumInstantiations, Expansion->getColonLoc())) + return StmtError(); + + // Collect shared statements. + SmallVector Shared; + if (Expansion->getInit()) + Shared.push_back(Expansion->getInit()); + + if (auto *Iter = dyn_cast(Expansion)) { + Shared.push_back(Iter->getRangeVarStmt()); + Shared.push_back(Iter->getBeginVarStmt()); + Shared.push_back(Iter->getEndVarStmt()); + } else if (auto *Destructuring = + dyn_cast(Expansion)) { + Shared.push_back(Destructuring->getDecompositionDeclStmt()); + } + + // Return an empty statement if the range is empty. + if (*NumInstantiations == 0) { + Expansion->getDecl()->setInstantiations( + CXXExpansionInstantiationStmt::Create( + Context, Expansion->getBeginLoc(), Expansion->getEndLoc(), + /*Instantiations=*/{}, Shared, + isa(Expansion))); + return Expansion; + } + + // Create a compound statement binding loop and body. + Stmt *VarAndBody[] = {Expansion->getExpansionVarStmt(), Body}; + Stmt *CombinedBody = + CompoundStmt::Create(Context, VarAndBody, FPOptionsOverride(), + Body->getBeginLoc(), Body->getEndLoc()); + + // Expand the body for each instantiation. + SmallVector Instantiations; + ExpansionStmtDecl *ESD = Expansion->getDecl(); + for (uint64_t I = 0; I < *NumInstantiations; ++I) { + // Now that we're expanding this, exit the context of the expansion stmt + // so that we no longer treat this as dependent. + ContextRAII CtxGuard(*this, CurContext->getParent(), + /*NewThis=*/false); + + TemplateArgument Arg{Context, llvm::APSInt::get(I), + Context.getPointerDiffType()}; + MultiLevelTemplateArgumentList MTArgList(ESD, Arg, true); + MTArgList.addOuterRetainedLevels( + Expansion->getDecl()->getIndexTemplateParm()->getDepth()); + + LocalInstantiationScope LIScope(*this, /*CombineWithOuterScope=*/true); + InstantiatingTemplate Inst(*this, Body->getBeginLoc(), Expansion, Arg, + Body->getSourceRange()); + + StmtResult Instantiation = SubstStmt(CombinedBody, MTArgList); + + if (Instantiation.isInvalid()) + return StmtError(); + Instantiations.push_back(Instantiation.get()); + } + + auto *InstantiationsStmt = CXXExpansionInstantiationStmt::Create( + Context, Expansion->getBeginLoc(), Expansion->getEndLoc(), Instantiations, + Shared, isa(Expansion)); + + Expansion->getDecl()->setInstantiations(InstantiationsStmt); + return Expansion; +} + +ExprResult +Sema::BuildCXXExpansionInitListSelectExpr(CXXExpansionInitListExpr *Range, + Expr *Idx) { + if (Range->containsPackExpansion() || Idx->isValueDependent()) + return new (Context) CXXExpansionInitListSelectExpr(Context, Range, Idx); + + // The index is a DRE to a template parameter; we should never + // fail to evaluate it. + Expr::EvalResult ER; + if (!Idx->EvaluateAsInt(ER, Context)) + llvm_unreachable("Failed to evaluate expansion index"); + + uint64_t I = ER.Val.getInt().getZExtValue(); + return Range->getExprs()[I]; +} + +ExprResult Sema::BuildCXXDestructuringExpansionSelectExpr(DecompositionDecl *DD, + Expr *Idx) { + if (Idx->isValueDependent()) + return new (Context) CXXDestructuringExpansionSelectExpr(Context, DD, Idx); + + Expr::EvalResult ER; + if (!Idx->EvaluateAsInt(ER, Context)) + llvm_unreachable("Failed to evaluate expansion index"); + + uint64_t I = ER.Val.getInt().getZExtValue(); + MarkAnyDeclReferenced(Idx->getBeginLoc(), DD, true); + if (auto *BD = DD->bindings()[I]; auto *HVD = BD->getHoldingVar()) + return HVD->getInit(); + else + return BD->getBinding(); +} + +std::optional +Sema::ComputeExpansionSize(CXXExpansionStmt *Expansion) { + assert(!Expansion->hasDependentSize()); + + if (isa(Expansion)) + return cast( + Expansion->getExpansionVariable()->getInit()) + ->getRangeExpr() + ->getExprs() + .size(); + + if (auto *Destructuring = dyn_cast(Expansion)) + return Destructuring->getDecompositionDecl()->bindings().size(); + + // By [stmt.expand]5.2, N is the result of evaluating the expression + // + // [] consteval { + // std::ptrdiff_t result = 0; + // for (auto i = begin; i != end; ++i) ++result; + // return result; + // }() + if (auto *Iterating = dyn_cast(Expansion)) { + EnterExpressionEvaluationContext ExprEvalCtx( + *this, ExpressionEvaluationContext::ConstantEvaluated); + + // FIXME: Actually do that; unfortunately, conjuring a lambda out of thin + // air in Sema is a massive pain, so for now just cheat by computing + // 'end - begin'. + SourceLocation Loc = Iterating->getColonLoc(); + DeclRefExpr *Begin = BuildDeclRefExpr( + Iterating->getBeginVar(), + Iterating->getBeginVar()->getType().getNonReferenceType(), VK_LValue, + Loc); + + DeclRefExpr *End = BuildDeclRefExpr( + Iterating->getEndVar(), + Iterating->getEndVar()->getType().getNonReferenceType(), VK_LValue, + Loc); + + ExprResult N = ActOnBinOp(getCurScope(), Loc, tok::minus, End, Begin); + if (N.isInvalid()) + return std::nullopt; + + Expr::EvalResult ER; + SmallVector Notes; + ER.Diag = &Notes; + if (!N.get()->EvaluateAsInt(ER, Context)) { + Diag(Loc, diag::err_expansion_size_expr_not_ice); + for (const auto &[Location, PDiag] : Notes) + Diag(Location, PDiag); + return std::nullopt; + } + + if (ER.Val.getInt().isNegative()) { + Diag(Loc, diag::err_expansion_size_negative) << ER.Val.getInt(); + return std::nullopt; + } + + return ER.Val.getInt().getZExtValue(); + } + + llvm_unreachable("Invalid expansion statement class"); +} diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index a50c27610dc96..b37f1b8253f4c 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -19302,11 +19302,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 fe1f89b7a5dfa..e93d4291fbf6f 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 fbc2e7eb30676..a37092c163b04 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..576ec6c80770e 100644 --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -4455,13 +4455,16 @@ 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); } LabelDecl *Sema::LookupOrCreateLabel(IdentifierInfo *II, SourceLocation Loc, - SourceLocation GnuLabelLoc) { + SourceLocation GnuLabelLoc, + bool ForLabelStmt) { if (GnuLabelLoc.isValid()) { // Local label definitions always shadow existing labels. auto *Res = LabelDecl::Create(Context, CurContext, Loc, II, GnuLabelLoc); @@ -4470,15 +4473,43 @@ LabelDecl *Sema::LookupOrCreateLabel(IdentifierInfo *II, SourceLocation Loc, return cast(Res); } - // Not a GNU local label. - LabelDecl *Res = LookupExistingLabel(II, Loc); - if (!Res) { - // If not forward referenced or defined already, create the backing decl. - Res = LabelDecl::Create(Context, CurContext, Loc, II); - Scope *S = CurScope->getFnParent(); - assert(S && "Not in a function?"); - PushOnScopeChains(Res, S, true); + LabelDecl *Existing = LookupExistingLabel(II, Loc); + + // C++26 [stmt.label]p4 An identifier label shall not be enclosed by an + // expansion-statement. + // + // As an extension, we allow GNU local labels since they are logically + // scoped to the containing block, which prevents us from ending up with + // multiple copies of the same label in a function after instantiation. + // + // While allowing this is slightly more complicated, it also has the nice + // side-effect of avoiding otherwise rather horrible diagnostics you'd get + // when trying to use '__label__' if we didn't support this. + if (ForLabelStmt && CurContext->isExpansionStmt()) { + if (Existing && Existing->isGnuLocal()) + return Existing; + + // Drop the label from the AST as creating it anyway would cause us to + // either issue various unhelpful diagnostics (if we were to declare + // it in the function decl context) or shadow a valid label with the + // same name outside the expansion statement. + Diag(Loc, diag::err_expansion_stmt_label); + return nullptr; } + + if (Existing) + return Existing; + + // Declare non-local labels outside any expansion statements; this is required + // to support jumping out of an expansion statement. + ContextRAII Ctx{*this, CurContext->getEnclosingNonExpansionStatementContext(), + /*NewThisContext=*/false}; + + // Not a GNU local label. Create the backing decl. + auto *Res = LabelDecl::Create(Context, CurContext, Loc, II); + Scope *S = CurScope->getFnParent(); + assert(S && "Not in a function?"); + PushOnScopeChains(Res, S, true); return Res; } diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index f39896336053e..14ebf7260ad6f 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -528,6 +528,25 @@ Sema::ActOnCaseExpr(SourceLocation CaseLoc, ExprResult Val) { return CheckAndFinish(Val.get()); } +static bool DiagnoseSwitchCaseInExpansionStmt(Sema &S, SourceLocation KwLoc, + bool IsDefault) { + // C++26 [stmt.expand] The compound-statement of an expansion-statement is a + // control-flow-limited statement. + // + // We diagnose this here rather than in JumpDiagnostics because those run + // after the expansion statement is instantiated, at which point we will have + // have already complained about duplicate case labels, which is not exactly + // great QOI. + if (S.CurContext->isExpansionStmt() && + S.getCurFunction()->SwitchStack.back().EnclosingDC != S.CurContext) { + S.Diag(KwLoc, diag::err_expansion_stmt_case) << IsDefault; + S.Diag(S.getCurFunction()->SwitchStack.back().getPointer()->getSwitchLoc(), + diag::note_enclosing_switch_statement_here); + return true; + } + return false; +} + StmtResult Sema::ActOnCaseStmt(SourceLocation CaseLoc, ExprResult LHSVal, SourceLocation DotDotDotLoc, ExprResult RHSVal, @@ -547,6 +566,9 @@ Sema::ActOnCaseStmt(SourceLocation CaseLoc, ExprResult LHSVal, return StmtError(); } + if (DiagnoseSwitchCaseInExpansionStmt(*this, CaseLoc, false)) + return StmtError(); + if (LangOpts.OpenACC && getCurScope()->isInOpenACCComputeConstructScope(Scope::SwitchScope)) { Diag(CaseLoc, diag::err_acc_branch_in_out_compute_construct) @@ -572,6 +594,9 @@ Sema::ActOnDefaultStmt(SourceLocation DefaultLoc, SourceLocation ColonLoc, return SubStmt; } + if (DiagnoseSwitchCaseInExpansionStmt(*this, DefaultLoc, true)) + return StmtError(); + if (LangOpts.OpenACC && getCurScope()->isInOpenACCComputeConstructScope(Scope::SwitchScope)) { Diag(DefaultLoc, diag::err_acc_branch_in_out_compute_construct) @@ -1196,8 +1221,9 @@ StmtResult Sema::ActOnStartOfSwitchStmt(SourceLocation SwitchLoc, auto *SS = SwitchStmt::Create(Context, InitStmt, Cond.get().first, CondExpr, LParenLoc, RParenLoc); + SS->setSwitchLoc(SwitchLoc); getCurFunction()->SwitchStack.push_back( - FunctionScopeInfo::SwitchInfo(SS, false)); + FunctionScopeInfo::SwitchInfo(SS, CurContext)); return SS; } @@ -1313,7 +1339,7 @@ Sema::ActOnFinishSwitchStmt(SourceLocation SwitchLoc, Stmt *Switch, BodyStmt = new (Context) NullStmt(BodyStmt->getBeginLoc()); } - SS->setBody(BodyStmt, SwitchLoc); + SS->setBody(BodyStmt); Expr *CondExpr = SS->getCond(); if (!CondExpr) return StmtError(); @@ -2409,8 +2435,13 @@ void NoteForRangeBeginEndFunction(Sema &SemaRef, Expr *E, } /// Build a variable declaration for a for-range statement. -VarDecl *BuildForRangeVarDecl(Sema &SemaRef, SourceLocation Loc, - QualType Type, StringRef Name) { +VarDecl *BuildForRangeVarDecl(Sema &SemaRef, SourceLocation Loc, QualType Type, + StringRef Name, bool ForExpansionStmt) { + // Making the variable constexpr doesn't automatically add 'const' to the + // type, so do that now. + if (ForExpansionStmt && !Type->isReferenceType()) + Type = Type.withConst(); + DeclContext *DC = SemaRef.CurContext; IdentifierInfo *II = &SemaRef.PP.getIdentifierTable().get(Name); TypeSourceInfo *TInfo = SemaRef.Context.getTrivialTypeSourceInfo(Type, Loc); @@ -2418,9 +2449,11 @@ VarDecl *BuildForRangeVarDecl(Sema &SemaRef, SourceLocation Loc, TInfo, SC_None); Decl->setImplicit(); Decl->setCXXForRangeImplicitVar(true); + if (ForExpansionStmt) + // CWG 3044: Do not make the variable 'static'. + Decl->setConstexpr(true); return Decl; } - } static bool ObjCEnumerationCollection(Expr *Collection) { @@ -2428,6 +2461,25 @@ static bool ObjCEnumerationCollection(Expr *Collection) { && Collection->getType()->getAs() != nullptr; } +StmtResult Sema::BuildCXXForRangeRangeVar(Scope *S, Expr *Range, + bool ForExpansionStmt) { + // Divide by 2, since the variables are in the inner scope (loop body). + const auto DepthStr = std::to_string(S->getDepth() / 2); + SourceLocation RangeLoc = Range->getBeginLoc(); + VarDecl *RangeVar = + BuildForRangeVarDecl(*this, RangeLoc, Context.getAutoRRefDeductType(), + std::string("__range") + DepthStr, ForExpansionStmt); + if (FinishForRangeVarDecl(*this, RangeVar, Range, RangeLoc, + diag::err_for_range_deduction_failure)) + + return StmtError(); + + // Claim the type doesn't contain auto: we've already done the checking. + DeclGroupPtrTy RangeGroup = + BuildDeclaratorGroup(MutableArrayRef((Decl **)&RangeVar, 1)); + return ActOnDeclStmt(RangeGroup, RangeLoc, RangeLoc); +} + StmtResult Sema::ActOnCXXForRangeStmt( Scope *S, SourceLocation ForLoc, SourceLocation CoawaitLoc, Stmt *InitStmt, Stmt *First, SourceLocation ColonLoc, Expr *Range, SourceLocation RParenLoc, @@ -2472,22 +2524,8 @@ StmtResult Sema::ActOnCXXForRangeStmt( } // Build auto && __range = range-init - // Divide by 2, since the variables are in the inner scope (loop body). - const auto DepthStr = std::to_string(S->getDepth() / 2); - SourceLocation RangeLoc = Range->getBeginLoc(); - VarDecl *RangeVar = BuildForRangeVarDecl(*this, RangeLoc, - Context.getAutoRRefDeductType(), - std::string("__range") + DepthStr); - if (FinishForRangeVarDecl(*this, RangeVar, Range, RangeLoc, - diag::err_for_range_deduction_failure)) { - ActOnInitializerError(LoopVar); - return StmtError(); - } - - // Claim the type doesn't contain auto: we've already done the checking. - DeclGroupPtrTy RangeGroup = - BuildDeclaratorGroup(MutableArrayRef((Decl **)&RangeVar, 1)); - StmtResult RangeDecl = ActOnDeclStmt(RangeGroup, RangeLoc, RangeLoc); + auto RangeDecl = + BuildCXXForRangeRangeVar(S, Range, /*ForExpansionStmt=*/false); if (RangeDecl.isInvalid()) { ActOnInitializerError(LoopVar); return StmtError(); @@ -2686,6 +2724,229 @@ static StmtResult RebuildForRangeWithDereference(Sema &SemaRef, Scope *S, AdjustedRange.get(), RParenLoc, Sema::BFRK_Rebuild); } +void Sema::ApplyForRangeOrExpansionStatementLifetimeExtension( + VarDecl *RangeVar, ArrayRef Temporaries) { + if (Temporaries.empty()) + return; + + InitializedEntity Entity = InitializedEntity::InitializeVariable(RangeVar); + for (auto *MTE : Temporaries) + MTE->setExtendingDecl(RangeVar, Entity.allocateManglingNumber()); +} + +Sema::ForRangeBeginEndInfo Sema::BuildCXXForRangeBeginEndVars( + Scope *S, VarDecl *RangeVar, SourceLocation ColonLoc, + SourceLocation CoawaitLoc, + ArrayRef LifetimeExtendTemps, + BuildForRangeKind Kind, bool ForExpansionStmt, StmtResult *RebuildResult, + llvm::function_ref RebuildWithDereference) { + QualType RangeVarType = RangeVar->getType(); + SourceLocation RangeLoc = RangeVar->getLocation(); + const QualType RangeVarNonRefType = RangeVarType.getNonReferenceType(); + + ExprResult BeginRangeRef = + BuildDeclRefExpr(RangeVar, RangeVarNonRefType, VK_LValue, ColonLoc); + if (BeginRangeRef.isInvalid()) + return {}; + + ExprResult EndRangeRef = + BuildDeclRefExpr(RangeVar, RangeVarNonRefType, VK_LValue, ColonLoc); + if (EndRangeRef.isInvalid()) + return {}; + + QualType AutoType = Context.getAutoDeductType(); + Expr *Range = RangeVar->getInit(); + if (!Range) + return {}; + QualType RangeType = Range->getType(); + + if (RequireCompleteType(RangeLoc, RangeType, + diag::err_for_range_incomplete_type)) + return {}; + + // P2718R0 - Lifetime extension in range-based for loops. + // + // CWG 3043 – Do not apply lifetime extension to iterating + // expansion statements. + if (getLangOpts().CPlusPlus23 && !ForExpansionStmt) + ApplyForRangeOrExpansionStatementLifetimeExtension(RangeVar, + LifetimeExtendTemps); + + // Build auto __begin = begin-expr, __end = end-expr. + // Divide by 2, since the variables are in the inner scope (loop body). + const auto DepthStr = std::to_string(S->getDepth() / 2); + VarDecl *BeginVar = + BuildForRangeVarDecl(*this, ColonLoc, AutoType, + std::string("__begin") + DepthStr, ForExpansionStmt); + VarDecl *EndVar = + BuildForRangeVarDecl(*this, ColonLoc, AutoType, + std::string("__end") + DepthStr, ForExpansionStmt); + + // Build begin-expr and end-expr and attach to __begin and __end variables. + ExprResult BeginExpr, EndExpr; + if (const ArrayType *UnqAT = RangeType->getAsArrayTypeUnsafe()) { + // - if _RangeT is an array type, begin-expr and end-expr are __range and + // __range + __bound, respectively, where __bound is the array bound. If + // _RangeT is an array of unknown size or an array of incomplete type, + // the program is ill-formed; + + // begin-expr is __range. + BeginExpr = BeginRangeRef; + if (!CoawaitLoc.isInvalid()) { + BeginExpr = ActOnCoawaitExpr(S, ColonLoc, BeginExpr.get()); + if (BeginExpr.isInvalid()) + return {}; + } + if (FinishForRangeVarDecl(*this, BeginVar, BeginRangeRef.get(), ColonLoc, + diag::err_for_range_iter_deduction_failure)) { + NoteForRangeBeginEndFunction(*this, BeginExpr.get(), BEF_begin); + return {}; + } + + // Find the array bound. + ExprResult BoundExpr; + if (const ConstantArrayType *CAT = dyn_cast(UnqAT)) + BoundExpr = IntegerLiteral::Create( + Context, CAT->getSize(), Context.getPointerDiffType(), RangeLoc); + else if (const VariableArrayType *VAT = + dyn_cast(UnqAT)) { + // For a variably modified type we can't just use the expression within + // the array bounds, since we don't want that to be re-evaluated here. + // Rather, we need to determine what it was when the array was first + // created - so we resort to using sizeof(vla)/sizeof(element). + // For e.g. + // void f(int b) { + // int vla[b]; + // b = -1; <-- This should not affect the num of iterations below + // for (int &c : vla) { .. } + // } + + // FIXME: This results in codegen generating IR that recalculates the + // run-time number of elements (as opposed to just using the IR Value + // that corresponds to the run-time value of each bound that was + // generated when the array was created.) If this proves too embarrassing + // even for unoptimized IR, consider passing a magic-value/cookie to + // codegen that then knows to simply use that initial llvm::Value (that + // corresponds to the bound at time of array creation) within + // getelementptr. But be prepared to pay the price of increasing a + // customized form of coupling between the two components - which could + // be hard to maintain as the codebase evolves. + + ExprResult SizeOfVLAExprR = ActOnUnaryExprOrTypeTraitExpr( + EndVar->getLocation(), UETT_SizeOf, + /*IsType=*/true, + CreateParsedType(VAT->desugar(), Context.getTrivialTypeSourceInfo( + VAT->desugar(), RangeLoc)) + .getAsOpaquePtr(), + EndVar->getSourceRange()); + if (SizeOfVLAExprR.isInvalid()) + return {}; + + ExprResult SizeOfEachElementExprR = ActOnUnaryExprOrTypeTraitExpr( + EndVar->getLocation(), UETT_SizeOf, + /*IsType=*/true, + CreateParsedType(VAT->desugar(), Context.getTrivialTypeSourceInfo( + VAT->getElementType(), RangeLoc)) + .getAsOpaquePtr(), + EndVar->getSourceRange()); + if (SizeOfEachElementExprR.isInvalid()) + return {}; + + BoundExpr = + ActOnBinOp(S, EndVar->getLocation(), tok::slash, SizeOfVLAExprR.get(), + SizeOfEachElementExprR.get()); + if (BoundExpr.isInvalid()) + return {}; + + } else { + // Can't be a DependentSizedArrayType or an IncompleteArrayType since + // UnqAT is not incomplete and Range is not type-dependent. + llvm_unreachable("Unexpected array type in for-range"); + } + + // end-expr is __range + __bound. + EndExpr = + ActOnBinOp(S, ColonLoc, tok::plus, EndRangeRef.get(), BoundExpr.get()); + if (EndExpr.isInvalid()) + return {}; + if (FinishForRangeVarDecl(*this, EndVar, EndExpr.get(), ColonLoc, + diag::err_for_range_iter_deduction_failure)) { + NoteForRangeBeginEndFunction(*this, EndExpr.get(), BEF_end); + return {}; + } + } else { + OverloadCandidateSet CandidateSet(RangeLoc, + OverloadCandidateSet::CSK_Normal); + BeginEndFunction BEFFailure; + ForRangeStatus RangeStatus = + BuildNonArrayForRange(*this, BeginRangeRef.get(), EndRangeRef.get(), + RangeType, BeginVar, EndVar, ColonLoc, CoawaitLoc, + &CandidateSet, &BeginExpr, &EndExpr, &BEFFailure); + + if (Kind == BFRK_Build && RangeStatus == FRS_NoViableFunction && + BEFFailure == BEF_begin) { + // If the range is being built from an array parameter, emit a + // a diagnostic that it is being treated as a pointer. + if (DeclRefExpr *DRE = dyn_cast(Range)) { + if (ParmVarDecl *PVD = dyn_cast(DRE->getDecl())) { + QualType ArrayTy = PVD->getOriginalType(); + QualType PointerTy = PVD->getType(); + if (PointerTy->isPointerType() && ArrayTy->isArrayType()) { + Diag(Range->getBeginLoc(), diag::err_range_on_array_parameter) + << RangeLoc << PVD << ArrayTy << PointerTy; + Diag(PVD->getLocation(), diag::note_declared_at); + return {}; + } + } + } + + // If building the range failed, try dereferencing the range expression + // unless a diagnostic was issued or the end function is problematic. + if (RebuildWithDereference) { + assert(RebuildResult); + StmtResult SR = RebuildWithDereference(); + if (SR.isInvalid() || SR.isUsable()) { + *RebuildResult = SR; + return {}; + } + } + } + + // Otherwise, emit diagnostics if we haven't already. + if (RangeStatus == FRS_NoViableFunction) { + Expr *Range = BEFFailure ? EndRangeRef.get() : BeginRangeRef.get(); + CandidateSet.NoteCandidates( + PartialDiagnosticAt(Range->getBeginLoc(), + PDiag(diag::err_for_range_invalid) + << RangeLoc << Range->getType() + << BEFFailure), + *this, OCD_AllCandidates, Range); + } + // Return an error if no fix was discovered. + if (RangeStatus != FRS_Success) + return {}; + } + + assert(!BeginExpr.isInvalid() && !EndExpr.isInvalid() && + "invalid range expression in for loop"); + + return {BeginVar, EndVar, BeginExpr.get(), EndExpr.get()}; +} + +void Sema::ActOnDependentForRangeInitializer(VarDecl *LoopVar, + BuildForRangeKind BFRK) { + // Deduce any 'auto's in the loop variable as 'DependentTy'. We'll fill + // them in properly when we instantiate the loop. + if (!LoopVar->isInvalidDecl() && BFRK != BFRK_Check) { + if (auto *DD = dyn_cast(LoopVar)) + for (auto *Binding : DD->bindings()) { + if (!Binding->isParameterPack()) + Binding->setType(Context.DependentTy); + } + LoopVar->setType(SubstAutoTypeDependent(LoopVar->getType())); + } +} + StmtResult Sema::BuildCXXForRangeStmt( SourceLocation ForLoc, SourceLocation CoawaitLoc, Stmt *InitStmt, SourceLocation ColonLoc, Stmt *RangeDecl, Stmt *Begin, Stmt *End, @@ -2717,216 +2978,36 @@ StmtResult Sema::BuildCXXForRangeStmt( if (RangeVarType->isDependentType()) { // The range is implicitly used as a placeholder when it is dependent. RangeVar->markUsed(Context); - - // Deduce any 'auto's in the loop variable as 'DependentTy'. We'll fill - // them in properly when we instantiate the loop. - if (!LoopVar->isInvalidDecl() && Kind != BFRK_Check) { - if (auto *DD = dyn_cast(LoopVar)) - for (auto *Binding : DD->bindings()) { - if (!Binding->isParameterPack()) - Binding->setType(Context.DependentTy); - } - LoopVar->setType(SubstAutoTypeDependent(LoopVar->getType())); - } + ActOnDependentForRangeInitializer(LoopVar, Kind); } else if (!BeginDeclStmt.get()) { - SourceLocation RangeLoc = RangeVar->getLocation(); - - const QualType RangeVarNonRefType = RangeVarType.getNonReferenceType(); - - ExprResult BeginRangeRef = BuildDeclRefExpr(RangeVar, RangeVarNonRefType, - VK_LValue, ColonLoc); - if (BeginRangeRef.isInvalid()) - return StmtError(); - - ExprResult EndRangeRef = BuildDeclRefExpr(RangeVar, RangeVarNonRefType, - VK_LValue, ColonLoc); - if (EndRangeRef.isInvalid()) - return StmtError(); + StmtResult RebuildResult; + auto RebuildWithDereference = [&] { + return RebuildForRangeWithDereference( + *this, S, ForLoc, CoawaitLoc, InitStmt, LoopVarDecl, ColonLoc, + RangeVar->getInit(), RangeVar->getLocation(), RParenLoc); + }; - QualType AutoType = Context.getAutoDeductType(); - Expr *Range = RangeVar->getInit(); - if (!Range) - return StmtError(); - QualType RangeType = Range->getType(); + auto BeginRangeRefTy = RangeVar->getType().getNonReferenceType(); + auto [BeginVar, EndVar, BeginExpr, EndExpr] = BuildCXXForRangeBeginEndVars( + S, RangeVar, ColonLoc, CoawaitLoc, LifetimeExtendTemps, Kind, + /*ForExpansionStmt=*/false, &RebuildResult, RebuildWithDereference); - if (RequireCompleteType(RangeLoc, RangeType, - diag::err_for_range_incomplete_type)) + if (!RebuildResult.isUnset()) + return RebuildResult; + if (!BeginVar || !EndVar) return StmtError(); - // P2718R0 - Lifetime extension in range-based for loops. - if (getLangOpts().CPlusPlus23 && !LifetimeExtendTemps.empty()) { - InitializedEntity Entity = - InitializedEntity::InitializeVariable(RangeVar); - for (auto *MTE : LifetimeExtendTemps) - MTE->setExtendingDecl(RangeVar, Entity.allocateManglingNumber()); - } - - // Build auto __begin = begin-expr, __end = end-expr. - // Divide by 2, since the variables are in the inner scope (loop body). - const auto DepthStr = std::to_string(S->getDepth() / 2); - VarDecl *BeginVar = BuildForRangeVarDecl(*this, ColonLoc, AutoType, - std::string("__begin") + DepthStr); - VarDecl *EndVar = BuildForRangeVarDecl(*this, ColonLoc, AutoType, - std::string("__end") + DepthStr); - - // Build begin-expr and end-expr and attach to __begin and __end variables. - ExprResult BeginExpr, EndExpr; - if (const ArrayType *UnqAT = RangeType->getAsArrayTypeUnsafe()) { - // - if _RangeT is an array type, begin-expr and end-expr are __range and - // __range + __bound, respectively, where __bound is the array bound. If - // _RangeT is an array of unknown size or an array of incomplete type, - // the program is ill-formed; - - // begin-expr is __range. - BeginExpr = BeginRangeRef; - if (!CoawaitLoc.isInvalid()) { - BeginExpr = ActOnCoawaitExpr(S, ColonLoc, BeginExpr.get()); - if (BeginExpr.isInvalid()) - return StmtError(); - } - if (FinishForRangeVarDecl(*this, BeginVar, BeginRangeRef.get(), ColonLoc, - diag::err_for_range_iter_deduction_failure)) { - NoteForRangeBeginEndFunction(*this, BeginExpr.get(), BEF_begin); - return StmtError(); - } - - // Find the array bound. - ExprResult BoundExpr; - if (const ConstantArrayType *CAT = dyn_cast(UnqAT)) - BoundExpr = IntegerLiteral::Create( - Context, CAT->getSize(), Context.getPointerDiffType(), RangeLoc); - else if (const VariableArrayType *VAT = - dyn_cast(UnqAT)) { - // For a variably modified type we can't just use the expression within - // the array bounds, since we don't want that to be re-evaluated here. - // Rather, we need to determine what it was when the array was first - // created - so we resort to using sizeof(vla)/sizeof(element). - // For e.g. - // void f(int b) { - // int vla[b]; - // b = -1; <-- This should not affect the num of iterations below - // for (int &c : vla) { .. } - // } - - // FIXME: This results in codegen generating IR that recalculates the - // run-time number of elements (as opposed to just using the IR Value - // that corresponds to the run-time value of each bound that was - // generated when the array was created.) If this proves too embarrassing - // even for unoptimized IR, consider passing a magic-value/cookie to - // codegen that then knows to simply use that initial llvm::Value (that - // corresponds to the bound at time of array creation) within - // getelementptr. But be prepared to pay the price of increasing a - // customized form of coupling between the two components - which could - // be hard to maintain as the codebase evolves. - - ExprResult SizeOfVLAExprR = ActOnUnaryExprOrTypeTraitExpr( - EndVar->getLocation(), UETT_SizeOf, - /*IsType=*/true, - CreateParsedType(VAT->desugar(), Context.getTrivialTypeSourceInfo( - VAT->desugar(), RangeLoc)) - .getAsOpaquePtr(), - EndVar->getSourceRange()); - if (SizeOfVLAExprR.isInvalid()) - return StmtError(); - - ExprResult SizeOfEachElementExprR = ActOnUnaryExprOrTypeTraitExpr( - EndVar->getLocation(), UETT_SizeOf, - /*IsType=*/true, - CreateParsedType(VAT->desugar(), - Context.getTrivialTypeSourceInfo( - VAT->getElementType(), RangeLoc)) - .getAsOpaquePtr(), - EndVar->getSourceRange()); - if (SizeOfEachElementExprR.isInvalid()) - return StmtError(); - - BoundExpr = - ActOnBinOp(S, EndVar->getLocation(), tok::slash, - SizeOfVLAExprR.get(), SizeOfEachElementExprR.get()); - if (BoundExpr.isInvalid()) - return StmtError(); - - } else { - // Can't be a DependentSizedArrayType or an IncompleteArrayType since - // UnqAT is not incomplete and Range is not type-dependent. - llvm_unreachable("Unexpected array type in for-range"); - } - - // end-expr is __range + __bound. - EndExpr = ActOnBinOp(S, ColonLoc, tok::plus, EndRangeRef.get(), - BoundExpr.get()); - if (EndExpr.isInvalid()) - return StmtError(); - if (FinishForRangeVarDecl(*this, EndVar, EndExpr.get(), ColonLoc, - diag::err_for_range_iter_deduction_failure)) { - NoteForRangeBeginEndFunction(*this, EndExpr.get(), BEF_end); - return StmtError(); - } - } else { - OverloadCandidateSet CandidateSet(RangeLoc, - OverloadCandidateSet::CSK_Normal); - BeginEndFunction BEFFailure; - ForRangeStatus RangeStatus = BuildNonArrayForRange( - *this, BeginRangeRef.get(), EndRangeRef.get(), RangeType, BeginVar, - EndVar, ColonLoc, CoawaitLoc, &CandidateSet, &BeginExpr, &EndExpr, - &BEFFailure); - - if (Kind == BFRK_Build && RangeStatus == FRS_NoViableFunction && - BEFFailure == BEF_begin) { - // If the range is being built from an array parameter, emit a - // a diagnostic that it is being treated as a pointer. - if (DeclRefExpr *DRE = dyn_cast(Range)) { - if (ParmVarDecl *PVD = dyn_cast(DRE->getDecl())) { - QualType ArrayTy = PVD->getOriginalType(); - QualType PointerTy = PVD->getType(); - if (PointerTy->isPointerType() && ArrayTy->isArrayType()) { - Diag(Range->getBeginLoc(), diag::err_range_on_array_parameter) - << RangeLoc << PVD << ArrayTy << PointerTy; - Diag(PVD->getLocation(), diag::note_declared_at); - return StmtError(); - } - } - } - - // If building the range failed, try dereferencing the range expression - // unless a diagnostic was issued or the end function is problematic. - StmtResult SR = RebuildForRangeWithDereference(*this, S, ForLoc, - CoawaitLoc, InitStmt, - LoopVarDecl, ColonLoc, - Range, RangeLoc, - RParenLoc); - if (SR.isInvalid() || SR.isUsable()) - return SR; - } - - // Otherwise, emit diagnostics if we haven't already. - if (RangeStatus == FRS_NoViableFunction) { - Expr *Range = BEFFailure ? EndRangeRef.get() : BeginRangeRef.get(); - CandidateSet.NoteCandidates( - PartialDiagnosticAt(Range->getBeginLoc(), - PDiag(diag::err_for_range_invalid) - << RangeLoc << Range->getType() - << BEFFailure), - *this, OCD_AllCandidates, Range); - } - // Return an error if no fix was discovered. - if (RangeStatus != FRS_Success) - return StmtError(); - } - - assert(!BeginExpr.isInvalid() && !EndExpr.isInvalid() && - "invalid range expression in for loop"); - // C++11 [dcl.spec.auto]p7: BeginType and EndType must be the same. // C++1z removes this restriction. + SourceLocation RangeLoc = RangeVar->getLocation(); QualType BeginType = BeginVar->getType(), EndType = EndVar->getType(); if (!Context.hasSameType(BeginType, EndType)) { Diag(RangeLoc, getLangOpts().CPlusPlus17 ? diag::warn_for_range_begin_end_types_differ : diag::ext_for_range_begin_end_types_differ) << BeginType << EndType; - NoteForRangeBeginEndFunction(*this, BeginExpr.get(), BEF_begin); - NoteForRangeBeginEndFunction(*this, EndExpr.get(), BEF_end); + NoteForRangeBeginEndFunction(*this, BeginExpr, BEF_begin); + NoteForRangeBeginEndFunction(*this, EndExpr, BEF_end); } BeginDeclStmt = @@ -2955,10 +3036,10 @@ StmtResult Sema::BuildCXXForRangeStmt( ActOnFinishFullExpr(NotEqExpr.get(), /*DiscardedValue*/ false); if (NotEqExpr.isInvalid()) { Diag(RangeLoc, diag::note_for_range_invalid_iterator) - << RangeLoc << 0 << BeginRangeRef.get()->getType(); - NoteForRangeBeginEndFunction(*this, BeginExpr.get(), BEF_begin); + << RangeLoc << 0 << BeginRangeRefTy; + NoteForRangeBeginEndFunction(*this, BeginExpr, BEF_begin); if (!Context.hasSameType(BeginType, EndType)) - NoteForRangeBeginEndFunction(*this, EndExpr.get(), BEF_end); + NoteForRangeBeginEndFunction(*this, EndExpr, BEF_end); return StmtError(); } @@ -2978,8 +3059,8 @@ StmtResult Sema::BuildCXXForRangeStmt( IncrExpr = ActOnFinishFullExpr(IncrExpr.get(), /*DiscardedValue*/ false); if (IncrExpr.isInvalid()) { Diag(RangeLoc, diag::note_for_range_invalid_iterator) - << RangeLoc << 2 << BeginRangeRef.get()->getType() ; - NoteForRangeBeginEndFunction(*this, BeginExpr.get(), BEF_begin); + << RangeLoc << 2 << BeginRangeRefTy; + NoteForRangeBeginEndFunction(*this, BeginExpr, BEF_begin); return StmtError(); } @@ -2992,8 +3073,8 @@ StmtResult Sema::BuildCXXForRangeStmt( ExprResult DerefExpr = ActOnUnaryOp(S, ColonLoc, tok::star, BeginRef.get()); if (DerefExpr.isInvalid()) { Diag(RangeLoc, diag::note_for_range_invalid_iterator) - << RangeLoc << 1 << BeginRangeRef.get()->getType(); - NoteForRangeBeginEndFunction(*this, BeginExpr.get(), BEF_begin); + << RangeLoc << 1 << BeginRangeRefTy; + NoteForRangeBeginEndFunction(*this, BeginExpr, BEF_begin); return StmtError(); } @@ -3003,7 +3084,7 @@ StmtResult Sema::BuildCXXForRangeStmt( AddInitializerToDecl(LoopVar, DerefExpr.get(), /*DirectInit=*/false); if (LoopVar->isInvalidDecl() || (LoopVar->getInit() && LoopVar->getInit()->containsErrors())) - NoteForRangeBeginEndFunction(*this, BeginExpr.get(), BEF_begin); + NoteForRangeBeginEndFunction(*this, BeginExpr, BEF_begin); } } diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index 7f858050db13e..0d56520d9733b 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -573,6 +573,7 @@ bool Sema::CodeSynthesisContext::isInstantiationRecord() const { case PriorTemplateArgumentSubstitution: case ConstraintsCheck: case NestedRequirementConstraintsCheck: + case ExpansionStmtInstantiation: return true; case RequirementInstantiation: @@ -770,6 +771,15 @@ Sema::InstantiatingTemplate::InstantiatingTemplate( PointOfInstantiation, InstantiationRange, /*Entity=*/nullptr, /*Template=*/nullptr, /*TemplateArgs=*/{}, &DeductionInfo) {} +Sema::InstantiatingTemplate::InstantiatingTemplate( + Sema &SemaRef, SourceLocation PointOfInstantiation, + CXXExpansionStmt *ExpansionStmt, ArrayRef TArgs, + SourceRange InstantiationRange) + : InstantiatingTemplate( + SemaRef, CodeSynthesisContext::ExpansionStmtInstantiation, + PointOfInstantiation, InstantiationRange, /*Entity=*/nullptr, + /*Template=*/nullptr, /*TemplateArgs=*/TArgs) {} + Sema::InstantiatingTemplate::InstantiatingTemplate( Sema &SemaRef, SourceLocation PointOfInstantiation, concepts::NestedRequirement *Req, ConstraintsCheck, @@ -1278,6 +1288,9 @@ void Sema::PrintInstantiationStack(InstantiationContextDiagFuncRef DiagFunc) { << /*isTemplateTemplateParam=*/true << Active->InstantiationRange); break; + case CodeSynthesisContext::ExpansionStmtInstantiation: + Diags.Report(Active->PointOfInstantiation, + diag::note_expansion_stmt_instantiation_here); } } } @@ -1306,6 +1319,7 @@ std::optional Sema::isSFINAEContext() const { case CodeSynthesisContext::ParameterMappingSubstitution: case CodeSynthesisContext::ConstraintNormalization: case CodeSynthesisContext::NestedRequirementConstraintsCheck: + case CodeSynthesisContext::ExpansionStmtInstantiation: // This is a template instantiation, so there is no SFINAE. return std::nullopt; case CodeSynthesisContext::LambdaExpressionSubstitution: @@ -1999,6 +2013,12 @@ Decl *TemplateInstantiator::TransformDecl(SourceLocation Loc, Decl *D) { maybeInstantiateFunctionParameterToScope(PVD)) return nullptr; + if (isa(D)) { + assert(SemaRef.CurrentInstantiationScope); + return cast( + *SemaRef.CurrentInstantiationScope->findInstantiationOf(D)); + } + return SemaRef.FindInstantiatedDecl(Loc, cast(D), TemplateArgs); } @@ -2393,9 +2413,13 @@ ExprResult TemplateInstantiator::TransformFunctionParmPackRefExpr(DeclRefExpr *E, ValueDecl *PD) { typedef LocalInstantiationScope::DeclArgumentPack DeclArgumentPack; - llvm::PointerUnion *Found - = getSema().CurrentInstantiationScope->findInstantiationOf(PD); - assert(Found && "no instantiation for parameter pack"); + llvm::PointerUnion *Found = + getSema().CurrentInstantiationScope->getInstantiationOfIfExists(PD); + + // This can happen when instantiating an expansion statement that contains + // a pack (e.g. `template for (auto x : {{ts...}})`). + if (!Found) + return E; Decl *TransformedDecl; if (DeclArgumentPack *Pack = dyn_cast(*Found)) { diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 28925cca8f956..2cebdcff01811 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -2070,6 +2070,41 @@ Decl *TemplateDeclInstantiator::VisitStaticAssertDecl(StaticAssertDecl *D) { InstantiatedMessageExpr.get(), D->getRParenLoc(), D->isFailed()); } +Decl * +TemplateDeclInstantiator::VisitExpansionStmtDecl(ExpansionStmtDecl *OldESD) { + Decl *Index = VisitNonTypeTemplateParmDecl(OldESD->getIndexTemplateParm()); + ExpansionStmtDecl *NewESD = SemaRef.BuildExpansionStmtDecl( + Owner, OldESD->getBeginLoc(), cast(Index)); + SemaRef.CurrentInstantiationScope->InstantiatedLocal(OldESD, NewESD); + + // If this was already expanded, only instantiate the expansion and + // don't touch the unexpanded expansion statement. + if (CXXExpansionInstantiationStmt *OldInst = OldESD->getInstantiations()) { + StmtResult NewInst = SemaRef.SubstStmt(OldInst, TemplateArgs); + if (NewInst.isInvalid()) + return nullptr; + + NewESD->setInstantiations(NewInst.getAs()); + NewESD->setExpansionPattern(OldESD->getExpansionPattern()); + return NewESD; + } + + // Enter the scope of this expansion statement; don't do this if we've + // already expanded it, as in that case we no longer want to treat its + // content as dependent. + Sema::ContextRAII Context(SemaRef, NewESD, /*NewThis=*/false); + + StmtResult Expansion = + SemaRef.SubstStmt(OldESD->getExpansionPattern(), TemplateArgs); + if (Expansion.isInvalid()) + return nullptr; + + // The code that handles CXXExpansionStmt takes care of calling + // setInstantiation() on the ESD if there was an expansion. + NewESD->setExpansionPattern(cast(Expansion.get())); + return NewESD; +} + Decl *TemplateDeclInstantiator::VisitEnumDecl(EnumDecl *D) { EnumDecl *PrevDecl = nullptr; if (EnumDecl *PatternPrev = getPreviousDeclForInstantiation(D)) { @@ -7061,6 +7096,12 @@ NamedDecl *Sema::FindInstantiatedDecl(SourceLocation Loc, NamedDecl *D, // anonymous unions in class templates). } + if (CurrentInstantiationScope) { + if (auto Found = CurrentInstantiationScope->getInstantiationOfIfExists(D)) + if (auto *FD = dyn_cast(cast(*Found))) + return FD; + } + if (!ParentDependsOnArgs) return D; diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp index 0f72d6a13ae06..3eecb67066a9d 100644 --- a/clang/lib/Sema/SemaTemplateVariadic.cpp +++ b/clang/lib/Sema/SemaTemplateVariadic.cpp @@ -909,10 +909,14 @@ bool Sema::CheckParameterPacksForExpansion( unsigned NewPackSize, PendingPackExpansionSize = 0; if (IsVarDeclPack) { // Figure out whether we're instantiating to an argument pack or not. + // + // The instantiation may not exist; this can happen when instantiating an + // expansion statement that contains a pack (e.g. + // `template for (auto x : {{ts...}})`). llvm::PointerUnion *Instantiation = - CurrentInstantiationScope->findInstantiationOf( + CurrentInstantiationScope->getInstantiationOfIfExists( cast(ParmPack.first)); - if (isa(*Instantiation)) { + if (Instantiation && isa(*Instantiation)) { // We could expand this function parameter pack. NewPackSize = cast(*Instantiation)->size(); } else { diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 0c8c1d18d317e..6c94ef0afd01c 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -857,6 +857,36 @@ class TreeTransform { StmtResult TransformOMPInformationalDirective(OMPExecutableDirective *S); + struct TransformCXXExpansionStmtResult { + ExpansionStmtDecl *NewESD{}; + Stmt *NewInit{}; + DeclStmt *NewExpansionVarDecl{}; + bool isValid() const { return NewESD != nullptr; } + }; + + TransformCXXExpansionStmtResult + TransformCXXExpansionStmtCommonParts(CXXExpansionStmt *S) { + Decl *ESD = + getDerived().TransformDecl(S->getDecl()->getLocation(), S->getDecl()); + if (!ESD || ESD->isInvalidDecl()) + return {}; + + Stmt *Init = S->getInit(); + if (Init) { + StmtResult SR = getDerived().TransformStmt(Init); + if (SR.isInvalid()) + return {}; + Init = SR.get(); + } + + StmtResult ExpansionVar = + getDerived().TransformStmt(S->getExpansionVarStmt()); + if (ExpansionVar.isInvalid()) + return {}; + + return {cast(ESD), Init, ExpansionVar.getAs()}; + } + // FIXME: We use LLVM_ATTRIBUTE_NOINLINE because inlining causes a ridiculous // amount of stack usage with clang. #define STMT(Node, Parent) \ @@ -9284,6 +9314,206 @@ TreeTransform::TransformCXXForRangeStmt(CXXForRangeStmt *S) { return FinishCXXForRangeStmt(NewStmt.get(), Body.get()); } +template +StmtResult TreeTransform::TransformCXXEnumeratingExpansionStmt( + CXXEnumeratingExpansionStmt *S) { + TransformCXXExpansionStmtResult Common = + TransformCXXExpansionStmtCommonParts(S); + if (!Common.isValid()) + return StmtError(); + + auto *Expansion = new (SemaRef.Context) CXXEnumeratingExpansionStmt( + Common.NewESD, Common.NewInit, Common.NewExpansionVarDecl, S->getForLoc(), + S->getLParenLoc(), S->getColonLoc(), S->getRParenLoc()); + + StmtResult Body = getDerived().TransformStmt(S->getBody()); + if (Body.isInvalid()) + return StmtError(); + + return SemaRef.FinishCXXExpansionStmt(Expansion, Body.get()); +} + +template +StmtResult TreeTransform::TransformCXXIteratingExpansionStmt( + CXXIteratingExpansionStmt *S) { + TransformCXXExpansionStmtResult Common = + TransformCXXExpansionStmtCommonParts(S); + if (!Common.isValid()) + return StmtError(); + + StmtResult Range = getDerived().TransformStmt(S->getRangeVarStmt()); + if (Range.isInvalid()) + return StmtError(); + + StmtResult Begin = getDerived().TransformStmt(S->getBeginVarStmt()); + StmtResult End = getDerived().TransformStmt(S->getEndVarStmt()); + if (Begin.isInvalid() || End.isInvalid()) + return StmtError(); + + auto *Expansion = new (SemaRef.Context) CXXIteratingExpansionStmt( + Common.NewESD, Common.NewInit, Common.NewExpansionVarDecl, + Range.getAs(), Begin.getAs(), End.getAs(), + S->getForLoc(), S->getLParenLoc(), S->getColonLoc(), S->getRParenLoc()); + + StmtResult Body = getDerived().TransformStmt(S->getBody()); + if (Body.isInvalid()) + return StmtError(); + + return SemaRef.FinishCXXExpansionStmt(Expansion, Body.get()); +} + +template +StmtResult TreeTransform::TransformCXXDependentExpansionStmt( + CXXDependentExpansionStmt *S) { + TransformCXXExpansionStmtResult Common; + ExprResult ExpansionInitializer; + SmallVector LifetimeExtendTemps; + + // Apply lifetime extension in case this ends up begin a destructuring + // expansion statement. + { + EnterExpressionEvaluationContext ExprEvalCtx( + SemaRef, SemaRef.currentEvaluationContext().Context); + SemaRef.currentEvaluationContext().InLifetimeExtendingContext = true; + SemaRef.currentEvaluationContext().RebuildDefaultArgOrDefaultInit = true; + + Common = TransformCXXExpansionStmtCommonParts(S); + if (!Common.isValid()) + return StmtError(); + + ExpansionInitializer = + getDerived().TransformExpr(S->getExpansionInitializer()); + if (ExpansionInitializer.isInvalid()) + return StmtError(); + + LifetimeExtendTemps = + SemaRef.currentEvaluationContext().ForRangeLifetimeExtendTemps; + } + + StmtResult Expansion = SemaRef.BuildNonEnumeratingCXXExpansionStmt( + Common.NewESD, Common.NewInit, Common.NewExpansionVarDecl, + ExpansionInitializer.get(), S->getForLoc(), S->getLParenLoc(), + S->getColonLoc(), S->getRParenLoc(), LifetimeExtendTemps); + if (Expansion.isInvalid()) + return StmtError(); + + StmtResult Body = getDerived().TransformStmt(S->getBody()); + if (Body.isInvalid()) + return StmtError(); + + return SemaRef.FinishCXXExpansionStmt(Expansion.get(), Body.get()); +} + +template +StmtResult TreeTransform::TransformCXXDestructuringExpansionStmt( + CXXDestructuringExpansionStmt *) { + // The only time we instantiate an expansion statement is if its expansion + // size is dependent (otherwise, we only instantiate the expansions and + // leave the underlying CXXExpansionStmt as-is). Since destructuring expansion + // statements never have a dependent size, we should never get here. + llvm_unreachable("Should never be instantiated"); +} + +template +ExprResult TreeTransform::TransformCXXExpansionInitListExpr( + CXXExpansionInitListExpr *E) { + bool ArgChanged = false; + SmallVector SubExprs; + if (getDerived().TransformExprs(E->getExprs().data(), E->getExprs().size(), + false, SubExprs, &ArgChanged)) + return ExprError(); + + if (!getDerived().AlwaysRebuild() && !ArgChanged) + return E; + + return CXXExpansionInitListExpr::Create(SemaRef.Context, SubExprs, + E->getLBraceLoc(), E->getRBraceLoc()); +} + +template +StmtResult TreeTransform::TransformCXXExpansionInstantiationStmt( + CXXExpansionInstantiationStmt *S) { + bool SubStmtChanged = false; + auto TransformStmts = [&](SmallVectorImpl &NewStmts, + ArrayRef OldStmts) { + for (Stmt *OldDS : OldStmts) { + StmtResult NewDS = getDerived().TransformStmt(OldDS); + if (NewDS.isInvalid()) + return true; + + SubStmtChanged |= NewDS.get() != OldDS; + NewStmts.push_back(NewDS.get()); + } + + return false; + }; + + SmallVector SharedStmts; + SmallVector Instantiations; + + // Apply lifetime extension to the shared statements in case this is a + // destructuring expansion statement (for other kinds of expansion + // statements, this should make no difference). + { + EnterExpressionEvaluationContext ExprEvalCtx( + SemaRef, SemaRef.currentEvaluationContext().Context); + SemaRef.currentEvaluationContext().InLifetimeExtendingContext = true; + SemaRef.currentEvaluationContext().RebuildDefaultArgOrDefaultInit = true; + if (TransformStmts(SharedStmts, S->getSharedStmts())) + return StmtError(); + + if (S->shouldApplyLifetimeExtensionToSharedStmts()) { + auto *VD = + cast(cast(SharedStmts.front())->getSingleDecl()); + SemaRef.ApplyForRangeOrExpansionStatementLifetimeExtension( + VD, SemaRef.currentEvaluationContext().ForRangeLifetimeExtendTemps); + } + } + + if (TransformStmts(Instantiations, S->getInstantiations())) + return StmtError(); + + if (!getDerived().AlwaysRebuild() && !SubStmtChanged) + return S; + + return CXXExpansionInstantiationStmt::Create( + SemaRef.Context, S->getBeginLoc(), S->getEndLoc(), Instantiations, + SharedStmts, S->shouldApplyLifetimeExtensionToSharedStmts()); +} + +template +ExprResult TreeTransform::TransformCXXExpansionInitListSelectExpr( + CXXExpansionInitListSelectExpr *E) { + ExprResult Range = getDerived().TransformExpr(E->getRangeExpr()); + ExprResult Idx = getDerived().TransformExpr(E->getIndexExpr()); + if (Range.isInvalid() || Idx.isInvalid()) + return ExprError(); + + if (!getDerived().AlwaysRebuild() && Range.get() == E->getRangeExpr() && + Idx.get() == E->getIndexExpr()) + return E; + + return SemaRef.BuildCXXExpansionInitListSelectExpr( + Range.getAs(), Idx.get()); +} + +template +ExprResult TreeTransform::TransformCXXDestructuringExpansionSelectExpr( + CXXDestructuringExpansionSelectExpr *E) { + Decl *DD = getDerived().TransformDecl( + E->getDecompositionDecl()->getLocation(), E->getDecompositionDecl()); + ExprResult Idx = getDerived().TransformExpr(E->getIndexExpr()); + if (!DD || Idx.isInvalid()) + return ExprError(); + + if (!getDerived().AlwaysRebuild() && DD == E->getDecompositionDecl() && + Idx.get() == E->getIndexExpr()) + return E; + + return SemaRef.BuildCXXDestructuringExpansionSelectExpr( + cast(DD), Idx.get()); +} + template StmtResult TreeTransform::TransformMSDependentExistsStmt( @@ -15596,7 +15826,7 @@ TreeTransform::TransformLambdaExpr(LambdaExpr *E) { // will be deemed as dependent even if there are no dependent template // arguments. // (A ClassTemplateSpecializationDecl is always a dependent context.) - while (DC->isRequiresExprBody()) + while (DC->isRequiresExprBody() || isa(DC)) DC = DC->getParent(); if ((getSema().isUnevaluatedContext() || getSema().isConstantEvaluatedContext()) && diff --git a/clang/lib/Serialization/ASTCommon.cpp b/clang/lib/Serialization/ASTCommon.cpp index 69db02f2efc40..a5e0baf80038f 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::ExpansionStmt: 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..e5498b0f98901 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 VisitExpansionStmtDecl(ExpansionStmtDecl *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::VisitExpansionStmtDecl(ExpansionStmtDecl *D) { + VisitDecl(D); + D->Expansion = cast(Record.readStmt()); + D->Instantiations = + cast_or_null(Record.readStmt()); + D->TParams = Record.readTemplateParameterList(); +} + 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 = ExpansionStmtDecl::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..715e6e5513e01 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -1730,6 +1730,78 @@ void ASTStmtReader::VisitCXXForRangeStmt(CXXForRangeStmt *S) { S->setBody(Record.readSubStmt()); } +void ASTStmtReader::VisitCXXExpansionStmt(CXXExpansionStmt *S) { + VisitStmt(S); + S->ForLoc = readSourceLocation(); + S->LParenLoc = readSourceLocation(); + S->ColonLoc = readSourceLocation(); + S->RParenLoc = readSourceLocation(); + S->ParentDecl = cast(Record.readDeclRef()); + S->setInit(Record.readSubStmt()); + S->setExpansionVarStmt(Record.readSubStmt()); + S->setBody(Record.readSubStmt()); +} + +void ASTStmtReader::VisitCXXExpansionInstantiationStmt( + CXXExpansionInstantiationStmt *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::VisitCXXEnumeratingExpansionStmt( + CXXEnumeratingExpansionStmt *S) { + VisitCXXExpansionStmt(S); +} + +void ASTStmtReader::VisitCXXIteratingExpansionStmt( + CXXIteratingExpansionStmt *S) { + VisitCXXExpansionStmt(S); + S->setRangeVarStmt(cast(Record.readSubStmt())); + S->setBeginVarStmt(cast(Record.readSubStmt())); + S->setEndVarStmt(cast(Record.readSubStmt())); +} + +void ASTStmtReader::VisitCXXDestructuringExpansionStmt( + CXXDestructuringExpansionStmt *S) { + VisitCXXExpansionStmt(S); + S->setDecompositionDeclStmt(Record.readSubStmt()); +} + +void ASTStmtReader::VisitCXXDependentExpansionStmt( + CXXDependentExpansionStmt *S) { + VisitCXXExpansionStmt(S); + S->setExpansionInitializer(Record.readSubExpr()); +} + +void ASTStmtReader::VisitCXXExpansionInitListExpr(CXXExpansionInitListExpr *E) { + VisitExpr(E); + assert(Record.peekInt() == E->getNumExprs() && "NumExprFields is wrong ?"); + Record.skipInts(1); + E->LBraceLoc = readSourceLocation(); + E->RBraceLoc = readSourceLocation(); + for (unsigned I = 0; I < E->getNumExprs(); ++I) + E->getExprs()[I] = Record.readSubExpr(); +} + +void ASTStmtReader::VisitCXXExpansionInitListSelectExpr( + CXXExpansionInitListSelectExpr *E) { + VisitExpr(E); + E->setRangeExpr(cast(Record.readSubExpr())); + E->setIndexExpr(Record.readSubExpr()); +} + +void ASTStmtReader::VisitCXXDestructuringExpansionSelectExpr( + CXXDestructuringExpansionSelectExpr *E) { + VisitExpr(E); + E->setDecompositionDecl(cast(Record.readDeclRef())); + E->setIndexExpr(Record.readSubExpr()); +} + void ASTStmtReader::VisitMSDependentExistsStmt(MSDependentExistsStmt *S) { VisitStmt(S); S->KeywordLoc = readSourceLocation(); @@ -3566,6 +3638,28 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) { /*numHandlers=*/Record[ASTStmtReader::NumStmtFields]); break; + case STMT_CXX_ENUMERATING_EXPANSION: + S = new (Context) CXXEnumeratingExpansionStmt(Empty); + break; + + case STMT_CXX_ITERATING_EXPANSION: + S = new (Context) CXXIteratingExpansionStmt(Empty); + break; + + case STMT_CXX_DESTRUCTURING_EXPANSION: + S = new (Context) CXXDestructuringExpansionStmt(Empty); + break; + + case STMT_CXX_DEPENDENT_EXPANSION: + S = new (Context) CXXDependentExpansionStmt(Empty); + break; + + case STMT_CXX_EXPANSION_INSTANTIATION: + S = CXXExpansionInstantiationStmt::CreateEmpty( + Context, Empty, Record[ASTStmtReader::NumStmtFields], + Record[ASTStmtReader::NumStmtFields + 1]); + break; + case STMT_CXX_FOR_RANGE: S = new (Context) CXXForRangeStmt(Empty); break; @@ -4443,6 +4537,20 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) { S = new (Context) ConceptSpecializationExpr(Empty); break; } + + case EXPR_CXX_EXPANSION_INIT_LIST: + S = CXXExpansionInitListExpr::CreateEmpty( + Context, Empty, Record[ASTStmtReader::NumExprFields]); + break; + + case EXPR_CXX_EXPANSION_INIT_LIST_SELECT: + S = new (Context) CXXExpansionInitListSelectExpr(Empty); + break; + + case EXPR_CXX_DESTRUCTURING_EXPANSION_SELECT: + S = new (Context) CXXDestructuringExpansionSelectExpr(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 a8c487005f6ec..733d5ec73cc67 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 VisitExpansionStmtDecl(ExpansionStmtDecl *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::VisitExpansionStmtDecl(ExpansionStmtDecl *D) { + VisitDecl(D); + Record.AddStmt(D->getExpansionPattern()); + Record.AddStmt(D->getInstantiations()); + Record.AddTemplateParameterList(D->getTemplateParameters()); + 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..d3f57716692f8 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -1704,6 +1704,86 @@ void ASTStmtWriter::VisitCXXForRangeStmt(CXXForRangeStmt *S) { Code = serialization::STMT_CXX_FOR_RANGE; } +void ASTStmtWriter::VisitCXXExpansionStmt(CXXExpansionStmt *S) { + VisitStmt(S); + Record.AddSourceLocation(S->getForLoc()); + Record.AddSourceLocation(S->getLParenLoc()); + Record.AddSourceLocation(S->getColonLoc()); + Record.AddSourceLocation(S->getRParenLoc()); + Record.AddDeclRef(S->getDecl()); + Record.AddStmt(S->getInit()); + Record.AddStmt(S->getExpansionVarStmt()); + Record.AddStmt(S->getBody()); +} + +void ASTStmtWriter::VisitCXXExpansionInstantiationStmt( + CXXExpansionInstantiationStmt *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::VisitCXXEnumeratingExpansionStmt( + CXXEnumeratingExpansionStmt *S) { + VisitCXXExpansionStmt(S); + Code = serialization::STMT_CXX_ENUMERATING_EXPANSION; +} + +void ASTStmtWriter::VisitCXXIteratingExpansionStmt( + CXXIteratingExpansionStmt *S) { + VisitCXXExpansionStmt(S); + Record.AddStmt(S->getRangeVarStmt()); + Record.AddStmt(S->getBeginVarStmt()); + Record.AddStmt(S->getEndVarStmt()); + Code = serialization::STMT_CXX_ITERATING_EXPANSION; +} + +void ASTStmtWriter::VisitCXXDestructuringExpansionStmt( + CXXDestructuringExpansionStmt *S) { + VisitCXXExpansionStmt(S); + Record.AddStmt(S->getDecompositionDeclStmt()); + Code = serialization::STMT_CXX_DESTRUCTURING_EXPANSION; +} + +void ASTStmtWriter::VisitCXXDependentExpansionStmt( + CXXDependentExpansionStmt *S) { + VisitCXXExpansionStmt(S); + Record.AddStmt(S->getExpansionInitializer()); + Code = serialization::STMT_CXX_DEPENDENT_EXPANSION; +} + +void ASTStmtWriter::VisitCXXExpansionInitListExpr(CXXExpansionInitListExpr *E) { + VisitExpr(E); + Record.push_back(E->getNumExprs()); + Record.AddSourceLocation(E->getLBraceLoc()); + Record.AddSourceLocation(E->getRBraceLoc()); + for (Expr *SubExpr : E->getExprs()) + Record.AddStmt(SubExpr); + Code = serialization::EXPR_CXX_EXPANSION_INIT_LIST; +} + +void ASTStmtWriter::VisitCXXExpansionInitListSelectExpr( + CXXExpansionInitListSelectExpr *E) { + VisitExpr(E); + Record.AddStmt(E->getRangeExpr()); + Record.AddStmt(E->getIndexExpr()); + Code = serialization::EXPR_CXX_EXPANSION_INIT_LIST_SELECT; +} + +void ASTStmtWriter::VisitCXXDestructuringExpansionSelectExpr( + CXXDestructuringExpansionSelectExpr *E) { + VisitExpr(E); + Record.AddDeclRef(E->getDecompositionDecl()); + Record.AddStmt(E->getIndexExpr()); + Code = serialization::EXPR_CXX_DESTRUCTURING_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 4e472b7fc38b0..f9286a50b1437 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1747,6 +1747,14 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::SEHExceptStmtClass: case Stmt::SEHLeaveStmtClass: case Stmt::SEHFinallyStmtClass: + case Stmt::CXXEnumeratingExpansionStmtClass: + case Stmt::CXXIteratingExpansionStmtClass: + case Stmt::CXXDestructuringExpansionStmtClass: + case Stmt::CXXDependentExpansionStmtClass: + case Stmt::CXXExpansionInstantiationStmtClass: + case Stmt::CXXExpansionInitListExprClass: + case Stmt::CXXExpansionInitListSelectExprClass: + case Stmt::CXXDestructuringExpansionSelectExprClass: case Stmt::OMPCanonicalLoopClass: case Stmt::OMPParallelDirectiveClass: case Stmt::OMPSimdDirectiveClass: diff --git a/clang/test/AST/ast-dump-expansion-stmt.cpp b/clang/test/AST/ast-dump-expansion-stmt.cpp new file mode 100644 index 0000000000000..1b947408f617e --- /dev/null +++ b/clang/test/AST/ast-dump-expansion-stmt.cpp @@ -0,0 +1,49 @@ +// Test without serialization: +// RUN: %clang_cc1 -std=c++26 -triple x86_64-unknown-unknown -ast-dump %s +// +// Test with serialization: +// RUN: %clang_cc1 -std=c++26 -triple x86_64-unknown-unknown -emit-pch -o %t %s +// RUN: %clang_cc1 -x c++ -std=c++26 -triple x86_64-unknown-unknown -include-pch %t -ast-dump-all /dev/null \ +// RUN: | sed -e "s/ //" -e "s/ imported//" + +template +struct Array { + T data[size]{}; + constexpr const T* begin() const { return data; } + constexpr const T* end() const { return data + size; } +}; + +void foo(int); + +template +void test(T t) { + // CHECK: ExpansionStmtDecl + // CHECK-NEXT: CXXEnumeratingExpansionStmt + // CHECK: CXXExpansionInstantiationStmt + template for (auto x : {1, 2, 3}) { + foo(x); + } + + // CHECK: ExpansionStmtDecl + // CHECK-NEXT: CXXIteratingExpansionStmt + // CHECK: CXXExpansionInstantiationStmt + static constexpr Array a; + template for (auto x : a) { + foo(x); + } + + // CHECK: ExpansionStmtDecl + // CHECK-NEXT: CXXDestructuringExpansionStmt + // CHECK: CXXExpansionInstantiationStmt + int arr[3]{1, 2, 3}; + template for (auto x : arr) { + foo(x); + } + + // CHECK: ExpansionStmtDecl + // CHECK-NEXT: CXXDependentExpansionStmt + // CHECK-NOT: CXXExpansionInstantiationStmt + template for (auto x : t) { + foo(x); + } +} diff --git a/clang/test/AST/ast-print-expansion-stmts.cpp b/clang/test/AST/ast-print-expansion-stmts.cpp new file mode 100644 index 0000000000000..bb9f79c6644c0 --- /dev/null +++ b/clang/test/AST/ast-print-expansion-stmts.cpp @@ -0,0 +1,104 @@ +// Without serialization: +// RUN: %clang_cc1 -std=c++26 -ast-print %s | FileCheck %s +// +// With serialization: +// RUN: %clang_cc1 -std=c++26 -emit-pch -o %t %s +// RUN: %clang_cc1 -x c++ -std=c++26 -include-pch %t -ast-print /dev/null | FileCheck %s + +template +struct Array { + T data[size]{}; + constexpr const T* begin() const { return data; } + constexpr const T* end() const { return data + size; } +}; + +// CHECK: void foo(int); +void foo(int); + +// CHECK: template void test(T t) { +template +void test(T t) { + // Enumerating expansion statement. + // + // CHECK: template for (auto x : { 1, 2, 3 }) { + // CHECK-NEXT: foo(x); + // CHECK-NEXT: } + template for (auto x : {1, 2, 3}) { + foo(x); + } + + // Iterating expansion statement. + // + // CHECK: static constexpr Array a; + // CHECK-NEXT: template for (auto x : a) { + // CHECK-NEXT: foo(x); + // CHECK-NEXT: } + static constexpr Array a; + template for (auto x : a) { + foo(x); + } + + // Destructuring expansion statement. + // + // CHECK: int arr[3]{1, 2, 3}; + // CHECK-NEXT: template for (auto x : arr) { + // CHECK-NEXT: foo(x); + // CHECK-NEXT: } + int arr[3]{1, 2, 3}; + template for (auto x : arr) { + foo(x); + } + + // Dependent expansion statement. + // + // CHECK: template for (auto x : t) { + // CHECK-NEXT: foo(x); + // CHECK-NEXT: } + template for (auto x : t) { + foo(x); + } +} + +// CHECK: template void test2(T t) { +template +void test2(T t) { + // Enumerating expansion statement. + // + // CHECK: template for (int x : { 1, 2, 3 }) { + // CHECK-NEXT: foo(x); + // CHECK-NEXT: } + template for (int x : {1, 2, 3}) { + foo(x); + } + + // Iterating expansion statement. + // + // CHECK: static constexpr Array a; + // CHECK-NEXT: template for (int x : a) { + // CHECK-NEXT: foo(x); + // CHECK-NEXT: } + static constexpr Array a; + template for (int x : a) { + foo(x); + } + + // Destructuring expansion statement. + // + // CHECK: int arr[3]{1, 2, 3}; + // CHECK-NEXT: template for (int x : arr) { + // CHECK-NEXT: foo(x); + // CHECK-NEXT: } + int arr[3]{1, 2, 3}; + template for (int x : arr) { + foo(x); + } + + // Dependent expansion statement. + // + // CHECK: template for (int x : t) { + // CHECK-NEXT: foo(x); + // CHECK-NEXT: } + template for (int x : t) { + foo(x); + } +} diff --git a/clang/test/CodeGenCXX/cxx2c-destructuring-expansion-stmt.cpp b/clang/test/CodeGenCXX/cxx2c-destructuring-expansion-stmt.cpp new file mode 100644 index 0000000000000..16d8c370a9d3f --- /dev/null +++ b/clang/test/CodeGenCXX/cxx2c-destructuring-expansion-stmt.cpp @@ -0,0 +1,471 @@ +// RUN: %clang_cc1 -std=c++2c -triple x86_64-unknown-linux-gnu -emit-llvm -o - %s | FileCheck %s + +struct A {}; +struct B { int x = 1; }; +struct C { int a = 1, b = 2, c = 3; }; + +void g(int); + +int references_destructuring() { + C c; + template for (auto& x : c) { ++x; } + template for (auto&& x : c) { ++x; } + return c.a + c.b + c.c; +} + +template +int destructure() { + int sum = 0; + template for (auto x : v) sum += x; + template for (constexpr auto x : v) sum += x; + return sum; +} + +void f() { + destructure(); + destructure(); + destructure(); +} + +void empty() { + static constexpr A a; + template for (auto x : A()) g(x); + template for (auto& x : a) g(x); + template for (auto&& x : A()) g(x); + template for (constexpr auto x : a) g(x); +} + +namespace apply_lifetime_extension { +struct T { + int& x; + T(int& x) noexcept : x(x) {} + ~T() noexcept { x = 42; } +}; + +const T& f(const T& t) noexcept { return t; } +T g(int& x) noexcept { return T(x); } + +// CWG 3043: +// +// Lifetime extension only applies to destructuring expansion statements +// (enumerating statements don't have a range variable, and the range variable +// of iterating statements is constexpr). +int lifetime_extension() { + int x = 5; + int sum = 0; + template for (auto e : f(g(x))) { + sum += x; + } + return sum + x; +} + +template +int lifetime_extension_instantiate_expansions() { + int x = 5; + int sum = 0; + template for (T e : f(g(x))) { + sum += x; + } + return sum + x; +} + +template +int lifetime_extension_dependent_expansion_stmt() { + int x = 5; + int sum = 0; + template for (int e : f(g((T&)x))) { + sum += x; + } + return sum + x; +} + +template +struct foo { + template + int lifetime_extension_multiple_instantiations() { + int x = 5; + int sum = 0; + template for (T e : f(g((U&)x))) { + sum += x; + } + return sum + x; + } +}; + +void instantiate() { + lifetime_extension_instantiate_expansions(); + lifetime_extension_dependent_expansion_stmt(); + foo().lifetime_extension_multiple_instantiations(); +} +} + +// CHECK: @_ZZ5emptyvE1a = internal constant %struct.A zeroinitializer, align 1 +// CHECK: @_ZTAXtl1BLi10EEE = {{.*}} constant %struct.B { i32 10 }, comdat +// CHECK: @_ZTAXtl1CLi1ELi2ELi3EEE = {{.*}} constant %struct.C { i32 1, i32 2, i32 3 }, comdat +// CHECK: @_ZTAXtl1CLi3ELi4ELi5EEE = {{.*}} constant %struct.C { i32 3, i32 4, i32 5 }, comdat + + +// CHECK-LABEL: define {{.*}} i32 @_Z24references_destructuringv() +// CHECK: entry: +// CHECK-NEXT: %c = alloca %struct.C, align 4 +// CHECK-NEXT: %0 = alloca ptr, align 8 +// CHECK-NEXT: %x = alloca ptr, align 8 +// CHECK-NEXT: %x1 = alloca ptr, align 8 +// CHECK-NEXT: %x4 = alloca ptr, align 8 +// CHECK-NEXT: %1 = alloca ptr, align 8 +// CHECK-NEXT: %x7 = alloca ptr, align 8 +// CHECK-NEXT: %x11 = alloca ptr, align 8 +// CHECK-NEXT: %x15 = alloca ptr, align 8 +// CHECK-NEXT: call void @_ZN1CC1Ev(ptr {{.*}} %c) +// CHECK-NEXT: store ptr %c, ptr %0, align 8 +// CHECK-NEXT: %2 = load ptr, ptr %0, align 8 +// CHECK-NEXT: %a = getelementptr inbounds nuw %struct.C, ptr %2, i32 0, i32 0 +// CHECK-NEXT: store ptr %a, ptr %x, align 8 +// CHECK-NEXT: %3 = load ptr, ptr %x, align 8 +// CHECK-NEXT: %4 = load i32, ptr %3, align 4 +// CHECK-NEXT: %inc = add nsw i32 %4, 1 +// CHECK-NEXT: store i32 %inc, ptr %3, align 4 +// CHECK-NEXT: br label %expand.next +// CHECK: expand.next: +// CHECK-NEXT: %5 = load ptr, ptr %0, align 8 +// CHECK-NEXT: %b = getelementptr inbounds nuw %struct.C, ptr %5, i32 0, i32 1 +// CHECK-NEXT: store ptr %b, ptr %x1, align 8 +// CHECK-NEXT: %6 = load ptr, ptr %x1, align 8 +// CHECK-NEXT: %7 = load i32, ptr %6, align 4 +// CHECK-NEXT: %inc2 = add nsw i32 %7, 1 +// CHECK-NEXT: store i32 %inc2, ptr %6, align 4 +// CHECK-NEXT: br label %expand.next3 +// CHECK: expand.next3: +// CHECK-NEXT: %8 = load ptr, ptr %0, align 8 +// CHECK-NEXT: %c5 = getelementptr inbounds nuw %struct.C, ptr %8, i32 0, i32 2 +// CHECK-NEXT: store ptr %c5, ptr %x4, align 8 +// CHECK-NEXT: %9 = load ptr, ptr %x4, align 8 +// CHECK-NEXT: %10 = load i32, ptr %9, align 4 +// CHECK-NEXT: %inc6 = add nsw i32 %10, 1 +// CHECK-NEXT: store i32 %inc6, ptr %9, align 4 +// CHECK-NEXT: br label %expand.end +// CHECK: expand.end: +// CHECK-NEXT: store ptr %c, ptr %1, align 8 +// CHECK-NEXT: %11 = load ptr, ptr %1, align 8 +// CHECK-NEXT: %a8 = getelementptr inbounds nuw %struct.C, ptr %11, i32 0, i32 0 +// CHECK-NEXT: store ptr %a8, ptr %x7, align 8 +// CHECK-NEXT: %12 = load ptr, ptr %x7, align 8 +// CHECK-NEXT: %13 = load i32, ptr %12, align 4 +// CHECK-NEXT: %inc9 = add nsw i32 %13, 1 +// CHECK-NEXT: store i32 %inc9, ptr %12, align 4 +// CHECK-NEXT: br label %expand.next10 +// CHECK: expand.next10: +// CHECK-NEXT: %14 = load ptr, ptr %1, align 8 +// CHECK-NEXT: %b12 = getelementptr inbounds nuw %struct.C, ptr %14, i32 0, i32 1 +// CHECK-NEXT: store ptr %b12, ptr %x11, align 8 +// CHECK-NEXT: %15 = load ptr, ptr %x11, align 8 +// CHECK-NEXT: %16 = load i32, ptr %15, align 4 +// CHECK-NEXT: %inc13 = add nsw i32 %16, 1 +// CHECK-NEXT: store i32 %inc13, ptr %15, align 4 +// CHECK-NEXT: br label %expand.next14 +// CHECK: expand.next14: +// CHECK-NEXT: %17 = load ptr, ptr %1, align 8 +// CHECK-NEXT: %c16 = getelementptr inbounds nuw %struct.C, ptr %17, i32 0, i32 2 +// CHECK-NEXT: store ptr %c16, ptr %x15, align 8 +// CHECK-NEXT: %18 = load ptr, ptr %x15, align 8 +// CHECK-NEXT: %19 = load i32, ptr %18, align 4 +// CHECK-NEXT: %inc17 = add nsw i32 %19, 1 +// CHECK-NEXT: store i32 %inc17, ptr %18, align 4 +// CHECK-NEXT: br label %expand.end18 +// CHECK: expand.end18: +// CHECK-NEXT: %a19 = getelementptr inbounds nuw %struct.C, ptr %c, i32 0, i32 0 +// CHECK-NEXT: %20 = load i32, ptr %a19, align 4 +// CHECK-NEXT: %b20 = getelementptr inbounds nuw %struct.C, ptr %c, i32 0, i32 1 +// CHECK-NEXT: %21 = load i32, ptr %b20, align 4 +// CHECK-NEXT: %add = add nsw i32 %20, %21 +// CHECK-NEXT: %c21 = getelementptr inbounds nuw %struct.C, ptr %c, i32 0, i32 2 +// CHECK-NEXT: %22 = load i32, ptr %c21, align 4 +// CHECK-NEXT: %add22 = add nsw i32 %add, %22 +// CHECK-NEXT: ret i32 %add22 + + +// CHECK-LABEL: define {{.*}} void @_Z1fv() +// CHECK: entry: +// CHECK-NEXT: %call = call {{.*}} i32 @_Z11destructureITnDaXtl1BLi10EEEEiv() +// CHECK-NEXT: %call1 = call {{.*}} i32 @_Z11destructureITnDaXtl1CLi1ELi2ELi3EEEEiv() +// CHECK-NEXT: %call2 = call {{.*}} i32 @_Z11destructureITnDaXtl1CLi3ELi4ELi5EEEEiv() +// CHECK-NEXT: ret void + + +// CHECK-LABEL: define {{.*}} i32 @_Z11destructureITnDaXtl1BLi10EEEEiv() +// CHECK: entry: +// CHECK-NEXT: %sum = alloca i32, align 4 +// CHECK-NEXT: %0 = alloca ptr, align 8 +// CHECK-NEXT: %x = alloca i32, align 4 +// CHECK-NEXT: %1 = alloca ptr, align 8 +// CHECK-NEXT: %x1 = alloca i32, align 4 +// CHECK-NEXT: store i32 0, ptr %sum, align 4 +// CHECK-NEXT: store ptr @_ZTAXtl1BLi10EEE, ptr %0, align 8 +// CHECK-NEXT: store i32 10, ptr %x, align 4 +// CHECK-NEXT: %2 = load i32, ptr %x, align 4 +// CHECK-NEXT: %3 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %add = add nsw i32 %3, %2 +// CHECK-NEXT: store i32 %add, ptr %sum, align 4 +// CHECK-NEXT: br label %expand.end +// CHECK: expand.end: +// CHECK-NEXT: store ptr @_ZTAXtl1BLi10EEE, ptr %1, align 8 +// CHECK-NEXT: store i32 10, ptr %x1, align 4 +// CHECK-NEXT: %4 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %add2 = add nsw i32 %4, 10 +// CHECK-NEXT: store i32 %add2, ptr %sum, align 4 +// CHECK-NEXT: br label %expand.end3 +// CHECK: expand.end3: +// CHECK-NEXT: %5 = load i32, ptr %sum, align 4 +// CHECK-NEXT: ret i32 %5 + + +// CHECK-LABEL: define {{.*}} i32 @_Z11destructureITnDaXtl1CLi1ELi2ELi3EEEEiv() +// CHECK: entry: +// CHECK-NEXT: %sum = alloca i32, align 4 +// CHECK-NEXT: %0 = alloca ptr, align 8 +// CHECK-NEXT: %x = alloca i32, align 4 +// CHECK-NEXT: %x1 = alloca i32, align 4 +// CHECK-NEXT: %x4 = alloca i32, align 4 +// CHECK-NEXT: %1 = alloca ptr, align 8 +// CHECK-NEXT: %x6 = alloca i32, align 4 +// CHECK-NEXT: %x9 = alloca i32, align 4 +// CHECK-NEXT: %x12 = alloca i32, align 4 +// CHECK-NEXT: store i32 0, ptr %sum, align 4 +// CHECK-NEXT: store ptr @_ZTAXtl1CLi1ELi2ELi3EEE, ptr %0, align 8 +// CHECK-NEXT: store i32 1, ptr %x, align 4 +// CHECK-NEXT: %2 = load i32, ptr %x, align 4 +// CHECK-NEXT: %3 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %add = add nsw i32 %3, %2 +// CHECK-NEXT: store i32 %add, ptr %sum, align 4 +// CHECK-NEXT: br label %expand.next +// CHECK: expand.next: +// CHECK-NEXT: store i32 2, ptr %x1, align 4 +// CHECK-NEXT: %4 = load i32, ptr %x1, align 4 +// CHECK-NEXT: %5 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %add2 = add nsw i32 %5, %4 +// CHECK-NEXT: store i32 %add2, ptr %sum, align 4 +// CHECK-NEXT: br label %expand.next3 +// CHECK: expand.next3: +// CHECK-NEXT: store i32 3, ptr %x4, align 4 +// CHECK-NEXT: %6 = load i32, ptr %x4, align 4 +// CHECK-NEXT: %7 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %add5 = add nsw i32 %7, %6 +// CHECK-NEXT: store i32 %add5, ptr %sum, align 4 +// CHECK-NEXT: br label %expand.end +// CHECK: expand.end: +// CHECK-NEXT: store ptr @_ZTAXtl1CLi1ELi2ELi3EEE, ptr %1, align 8 +// CHECK-NEXT: store i32 1, ptr %x6, align 4 +// CHECK-NEXT: %8 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %add7 = add nsw i32 %8, 1 +// CHECK-NEXT: store i32 %add7, ptr %sum, align 4 +// CHECK-NEXT: br label %expand.next8 +// CHECK: expand.next8: +// CHECK-NEXT: store i32 2, ptr %x9, align 4 +// CHECK-NEXT: %9 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %add10 = add nsw i32 %9, 2 +// CHECK-NEXT: store i32 %add10, ptr %sum, align 4 +// CHECK-NEXT: br label %expand.next11 +// CHECK: expand.next11: +// CHECK-NEXT: store i32 3, ptr %x12, align 4 +// CHECK-NEXT: %10 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %add13 = add nsw i32 %10, 3 +// CHECK-NEXT: store i32 %add13, ptr %sum, align 4 +// CHECK-NEXT: br label %expand.end14 +// CHECK: expand.end14: +// CHECK-NEXT: %11 = load i32, ptr %sum, align 4 +// CHECK-NEXT: ret i32 %11 + + +// CHECK-LABEL: define {{.*}} i32 @_Z11destructureITnDaXtl1CLi3ELi4ELi5EEEEiv() +// CHECK: entry: +// CHECK-NEXT: %sum = alloca i32, align 4 +// CHECK-NEXT: %0 = alloca ptr, align 8 +// CHECK-NEXT: %x = alloca i32, align 4 +// CHECK-NEXT: %x1 = alloca i32, align 4 +// CHECK-NEXT: %x4 = alloca i32, align 4 +// CHECK-NEXT: %1 = alloca ptr, align 8 +// CHECK-NEXT: %x6 = alloca i32, align 4 +// CHECK-NEXT: %x9 = alloca i32, align 4 +// CHECK-NEXT: %x12 = alloca i32, align 4 +// CHECK-NEXT: store i32 0, ptr %sum, align 4 +// CHECK-NEXT: store ptr @_ZTAXtl1CLi3ELi4ELi5EEE, ptr %0, align 8 +// CHECK-NEXT: store i32 3, ptr %x, align 4 +// CHECK-NEXT: %2 = load i32, ptr %x, align 4 +// CHECK-NEXT: %3 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %add = add nsw i32 %3, %2 +// CHECK-NEXT: store i32 %add, ptr %sum, align 4 +// CHECK-NEXT: br label %expand.next +// CHECK: expand.next: +// CHECK-NEXT: store i32 4, ptr %x1, align 4 +// CHECK-NEXT: %4 = load i32, ptr %x1, align 4 +// CHECK-NEXT: %5 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %add2 = add nsw i32 %5, %4 +// CHECK-NEXT: store i32 %add2, ptr %sum, align 4 +// CHECK-NEXT: br label %expand.next3 +// CHECK: expand.next3: +// CHECK-NEXT: store i32 5, ptr %x4, align 4 +// CHECK-NEXT: %6 = load i32, ptr %x4, align 4 +// CHECK-NEXT: %7 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %add5 = add nsw i32 %7, %6 +// CHECK-NEXT: store i32 %add5, ptr %sum, align 4 +// CHECK-NEXT: br label %expand.end +// CHECK: expand.end: +// CHECK-NEXT: store ptr @_ZTAXtl1CLi3ELi4ELi5EEE, ptr %1, align 8 +// CHECK-NEXT: store i32 3, ptr %x6, align 4 +// CHECK-NEXT: %8 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %add7 = add nsw i32 %8, 3 +// CHECK-NEXT: store i32 %add7, ptr %sum, align 4 +// CHECK-NEXT: br label %expand.next8 +// CHECK: expand.next8: +// CHECK-NEXT: store i32 4, ptr %x9, align 4 +// CHECK-NEXT: %9 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %add10 = add nsw i32 %9, 4 +// CHECK-NEXT: store i32 %add10, ptr %sum, align 4 +// CHECK-NEXT: br label %expand.next11 +// CHECK: expand.next11: +// CHECK-NEXT: store i32 5, ptr %x12, align 4 +// CHECK-NEXT: %10 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %add13 = add nsw i32 %10, 5 +// CHECK-NEXT: store i32 %add13, ptr %sum, align 4 +// CHECK-NEXT: br label %expand.end14 +// CHECK: expand.end14: +// CHECK-NEXT: %11 = load i32, ptr %sum, align 4 +// CHECK-NEXT: ret i32 %11 + + +// CHECK-LABEL: define {{.*}} void @_Z5emptyv() +// CHECK: entry: +// CHECK-NEXT: %0 = alloca ptr, align 8 +// CHECK-NEXT: %ref.tmp = alloca %struct.A, align 1 +// CHECK-NEXT: %1 = alloca ptr, align 8 +// CHECK-NEXT: %2 = alloca ptr, align 8 +// CHECK-NEXT: %ref.tmp1 = alloca %struct.A, align 1 +// CHECK-NEXT: %3 = alloca ptr, align 8 +// CHECK-NEXT: store ptr %ref.tmp, ptr %0, align 8 +// CHECK-NEXT: store ptr @_ZZ5emptyvE1a, ptr %1, align 8 +// CHECK-NEXT: store ptr %ref.tmp1, ptr %2, align 8 +// CHECK-NEXT: store ptr @_ZZ5emptyvE1a, ptr %3, align 8 +// CHECK-NEXT: ret void + + +// CHECK-LABEL: define {{.*}} i32 @_ZN24apply_lifetime_extension18lifetime_extensionEv() +// CHECK: entry: +// CHECK-NEXT: %x = alloca i32, align 4 +// CHECK-NEXT: %sum = alloca i32, align 4 +// CHECK-NEXT: %0 = alloca ptr, align 8 +// CHECK: %ref.tmp = alloca %"struct.apply_lifetime_extension::T", align 8 +// CHECK-NEXT: %e = alloca i32, align 4 +// CHECK-NEXT: store i32 5, ptr %x, align 4 +// CHECK-NEXT: store i32 0, ptr %sum, align 4 +// CHECK: call void @_ZN24apply_lifetime_extension1gERi(ptr dead_on_unwind writable sret(%"struct.apply_lifetime_extension::T") align 8 %ref.tmp, ptr {{.*}} %x) +// CHECK-NEXT: %call = call {{.*}} ptr @_ZN24apply_lifetime_extension1fERKNS_1TE(ptr {{.*}} %ref.tmp) +// CHECK-NEXT: store ptr %call, ptr %0, align 8 +// CHECK-NEXT: %1 = load ptr, ptr %0, align 8 +// CHECK: %x1 = getelementptr inbounds nuw %"struct.apply_lifetime_extension::T", ptr %1, i32 0, i32 0 +// CHECK-NEXT: %2 = load ptr, ptr %x1, align 8 +// CHECK-NEXT: %3 = load i32, ptr %2, align 4 +// CHECK-NEXT: store i32 %3, ptr %e, align 4 +// CHECK-NEXT: %4 = load i32, ptr %x, align 4 +// CHECK-NEXT: %5 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %add = add nsw i32 %5, %4 +// CHECK-NEXT: store i32 %add, ptr %sum, align 4 +// CHECK-NEXT: br label %expand.end +// CHECK: expand.end: +// CHECK-NEXT: call void @_ZN24apply_lifetime_extension1TD1Ev(ptr {{.*}} %ref.tmp) +// CHECK-NEXT: %6 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %7 = load i32, ptr %x, align 4 +// CHECK-NEXT: %add2 = add nsw i32 %6, %7 +// CHECK-NEXT: ret i32 %add2 + + +// CHECK-LABEL: define {{.*}} i32 @_ZN24apply_lifetime_extension41lifetime_extension_instantiate_expansionsIiEEiv() +// CHECK: entry: +// CHECK-NEXT: %x = alloca i32, align 4 +// CHECK-NEXT: %sum = alloca i32, align 4 +// CHECK-NEXT: %0 = alloca ptr, align 8 +// CHECK: %ref.tmp = alloca %"struct.apply_lifetime_extension::T", align 8 +// CHECK-NEXT: %e = alloca i32, align 4 +// CHECK-NEXT: store i32 5, ptr %x, align 4 +// CHECK-NEXT: store i32 0, ptr %sum, align 4 +// CHECK: call void @_ZN24apply_lifetime_extension1gERi(ptr dead_on_unwind writable sret(%"struct.apply_lifetime_extension::T") align 8 %ref.tmp, ptr {{.*}} %x) +// CHECK-NEXT: %call = call {{.*}} ptr @_ZN24apply_lifetime_extension1fERKNS_1TE(ptr {{.*}} %ref.tmp) +// CHECK-NEXT: store ptr %call, ptr %0, align 8 +// CHECK-NEXT: %1 = load ptr, ptr %0, align 8 +// CHECK: %x1 = getelementptr inbounds nuw %"struct.apply_lifetime_extension::T", ptr %1, i32 0, i32 0 +// CHECK-NEXT: %2 = load ptr, ptr %x1, align 8 +// CHECK-NEXT: %3 = load i32, ptr %2, align 4 +// CHECK-NEXT: store i32 %3, ptr %e, align 4 +// CHECK-NEXT: %4 = load i32, ptr %x, align 4 +// CHECK-NEXT: %5 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %add = add nsw i32 %5, %4 +// CHECK-NEXT: store i32 %add, ptr %sum, align 4 +// CHECK-NEXT: br label %expand.end +// CHECK: expand.end: +// CHECK-NEXT: call void @_ZN24apply_lifetime_extension1TD1Ev(ptr {{.*}} %ref.tmp) +// CHECK-NEXT: %6 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %7 = load i32, ptr %x, align 4 +// CHECK-NEXT: %add2 = add nsw i32 %6, %7 +// CHECK-NEXT: ret i32 %add2 + + +// CHECK-LABEL: define {{.*}} i32 @_ZN24apply_lifetime_extension43lifetime_extension_dependent_expansion_stmtIiEEiv() +// CHECK: entry: +// CHECK-NEXT: %x = alloca i32, align 4 +// CHECK-NEXT: %sum = alloca i32, align 4 +// CHECK-NEXT: %0 = alloca ptr, align 8 +// CHECK: %ref.tmp = alloca %"struct.apply_lifetime_extension::T", align 8 +// CHECK-NEXT: %e = alloca i32, align 4 +// CHECK-NEXT: store i32 5, ptr %x, align 4 +// CHECK-NEXT: store i32 0, ptr %sum, align 4 +// CHECK: call void @_ZN24apply_lifetime_extension1gERi(ptr dead_on_unwind writable sret(%"struct.apply_lifetime_extension::T") align 8 %ref.tmp, ptr {{.*}} %x) +// CHECK-NEXT: %call = call {{.*}} ptr @_ZN24apply_lifetime_extension1fERKNS_1TE(ptr {{.*}} %ref.tmp) +// CHECK-NEXT: store ptr %call, ptr %0, align 8 +// CHECK-NEXT: %1 = load ptr, ptr %0, align 8 +// CHECK: %x1 = getelementptr inbounds nuw %"struct.apply_lifetime_extension::T", ptr %1, i32 0, i32 0 +// CHECK-NEXT: %2 = load ptr, ptr %x1, align 8 +// CHECK-NEXT: %3 = load i32, ptr %2, align 4 +// CHECK-NEXT: store i32 %3, ptr %e, align 4 +// CHECK-NEXT: %4 = load i32, ptr %x, align 4 +// CHECK-NEXT: %5 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %add = add nsw i32 %5, %4 +// CHECK-NEXT: store i32 %add, ptr %sum, align 4 +// CHECK-NEXT: br label %expand.end +// CHECK: expand.end: +// CHECK-NEXT: call void @_ZN24apply_lifetime_extension1TD1Ev(ptr {{.*}} %ref.tmp) +// CHECK-NEXT: %6 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %7 = load i32, ptr %x, align 4 +// CHECK-NEXT: %add2 = add nsw i32 %6, %7 +// CHECK-NEXT: ret i32 %add2 + + +// CHECK-LABEL: define {{.*}} i32 @_ZN24apply_lifetime_extension3fooIiE42lifetime_extension_multiple_instantiationsIiEEiv(ptr {{.*}} %this) +// CHECK: entry: +// CHECK-NEXT: %this.addr = alloca ptr, align 8 +// CHECK-NEXT: %x = alloca i32, align 4 +// CHECK-NEXT: %sum = alloca i32, align 4 +// CHECK-NEXT: %0 = alloca ptr, align 8 +// CHECK: %ref.tmp = alloca %"struct.apply_lifetime_extension::T", align 8 +// CHECK-NEXT: %e = alloca i32, align 4 +// CHECK-NEXT: store ptr %this, ptr %this.addr, align 8 +// CHECK-NEXT: %this1 = load ptr, ptr %this.addr, align 8 +// CHECK-NEXT: store i32 5, ptr %x, align 4 +// CHECK-NEXT: store i32 0, ptr %sum, align 4 +// CHECK: call void @_ZN24apply_lifetime_extension1gERi(ptr dead_on_unwind writable sret(%"struct.apply_lifetime_extension::T") align 8 %ref.tmp, ptr {{.*}} %x) +// CHECK-NEXT: %call = call {{.*}} ptr @_ZN24apply_lifetime_extension1fERKNS_1TE(ptr {{.*}} %ref.tmp) +// CHECK-NEXT: store ptr %call, ptr %0, align 8 +// CHECK-NEXT: %1 = load ptr, ptr %0, align 8 +// CHECK: %x2 = getelementptr inbounds nuw %"struct.apply_lifetime_extension::T", ptr %1, i32 0, i32 0 +// CHECK-NEXT: %2 = load ptr, ptr %x2, align 8 +// CHECK-NEXT: %3 = load i32, ptr %2, align 4 +// CHECK-NEXT: store i32 %3, ptr %e, align 4 +// CHECK-NEXT: %4 = load i32, ptr %x, align 4 +// CHECK-NEXT: %5 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %add = add nsw i32 %5, %4 +// CHECK-NEXT: store i32 %add, ptr %sum, align 4 +// CHECK-NEXT: br label %expand.end +// CHECK: expand.end: +// CHECK-NEXT: call void @_ZN24apply_lifetime_extension1TD1Ev(ptr {{.*}} %ref.tmp) +// CHECK-NEXT: %6 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %7 = load i32, ptr %x, align 4 +// CHECK-NEXT: %add3 = add nsw i32 %6, %7 +// CHECK-NEXT: ret i32 %add3 diff --git a/clang/test/CodeGenCXX/cxx2c-enumerating-expansion-statements.cpp b/clang/test/CodeGenCXX/cxx2c-enumerating-expansion-statements.cpp new file mode 100644 index 0000000000000..c82b345de206b --- /dev/null +++ b/clang/test/CodeGenCXX/cxx2c-enumerating-expansion-statements.cpp @@ -0,0 +1,1518 @@ +// RUN: %clang_cc1 -std=c++2c -triple x86_64-unknown-linux-gnu -emit-llvm -o - %s | FileCheck %s + +struct S { + int x; + constexpr S(int x) : x{x} {} +}; + +void g(int); +void g(long); +void g(const char*); +void g(S); + +template constexpr int tg() { return n; } + +void h(int, int); + +void f1() { + template for (auto x : {1, 2, 3}) g(x); +} + +void f2() { + template for (auto x : {1, "123", S(45)}) g(x); +} + +void f3() { + template for (auto x : {}) g(x); +} + +void f4() { + template for (auto x : {1, 2}) + template for (auto y : {3, 4}) + h(x, y); +} + +void f5() { + template for (auto x : {}) static_assert(false, "discarded"); + template for (constexpr auto x : {}) static_assert(false, "discarded"); + template for (auto x : {1}) g(x); + template for (auto x : {2, 3, 4}) g(x); + template for (constexpr auto x : {5}) g(x); + template for (constexpr auto x : {6, 7, 8}) g(x); + template for (constexpr auto x : {9}) tg(); + template for (constexpr auto x : {10, 11, 12}) + static_assert(tg()); + + template for (int x : {13, 14, 15}) g(x); + template for (S x : {16, 17, 18}) g(x.x); + template for (constexpr S x : {19, 20, 21}) tg(); +} + +template +void t1() { + template for (T x : {}) g(x); + template for (constexpr T x : {}) g(x); + template for (auto x : {}) g(x); + template for (constexpr auto x : {}) g(x); + template for (T x : {1, 2}) g(x); + template for (T x : {T(3), T(4)}) g(x); + template for (auto x : {T(5), T(6)}) g(x); + template for (constexpr T x : {T(7), T(8)}) static_assert(tg()); + template for (constexpr auto x : {T(9), T(10)}) static_assert(tg()); +} + +template +struct s1 { + template + void tf() { + template for (T x : {}) g(x); + template for (constexpr T x : {}) g(x); + template for (U x : {}) g(x); + template for (constexpr U x : {}) g(x); + template for (auto x : {}) g(x); + template for (constexpr auto x : {}) g(x); + template for (T x : {1, 2}) g(x); + template for (U x : {3, 4}) g(x); + template for (U x : {T(5), T(6)}) g(x); + template for (T x : {U(7), U(8)}) g(x); + template for (auto x : {T(9), T(10)}) g(x); + template for (auto x : {U(11), T(12)}) g(x); + template for (constexpr U x : {T(13), T(14)}) static_assert(tg()); + template for (constexpr T x : {U(15), U(16)}) static_assert(tg()); + template for (constexpr auto x : {T(17), U(18)}) static_assert(tg()); + } +}; + +template +void t2() { + template for (T x : {}) g(x); +} + +void f6() { + t1(); + t1(); + s1().tf(); + s1().tf(); + s1().tf(); + s1().tf(); + t2(); + t2(); + t2(); +} + +struct X { + int a, b, c; +}; + +template +void t3(Ts... ts) { + template for (auto x : {ts...}) g(x); + template for (auto x : {1, ts..., 2, ts..., 3}) g(x); + template for (auto x : {4, ts..., ts..., 5}) g(x); + template for (X x : {{ts...}, {ts...}, {6, 7, 8}}) g(x.a); + template for (X x : {X{ts...}}) g(x.a); +} + +template +void t4() { + template for (constexpr auto x : {is...}) { + g(x); + tg(); + } + + template for (constexpr auto x : {1, is..., 2, is..., 3}) { + g(x); + tg(); + } + + template for (constexpr auto x : {4, is..., is..., 5}) { + g(x); + tg(); + } + + template for (constexpr X x : {{is...}, {is...}, {6, 7, 8}}) { + g(x.a); + tg(); + } + + template for (constexpr X x : {X{is...}}) { + g(x.a); + tg(); + } +} + +template +struct s2 { + template + void tf() { + template for (auto x : {is..., js...}) g(x); + template for (X x : {{is...}, {js...}}) g(x.a); + template for (constexpr auto x : {is..., js...}) tg(); + template for (constexpr X x : {{is...}, {js...}}) tg(); + } +}; + +void f7() { + t3(42, 43, 44); + t4<42, 43, 44>(); + s2<1, 2, 3>().tf<4, 5, 6>(); +} + +template +void t5() { + ([] { + template for (constexpr auto x : {is}) { + g(x); + tg(); + } + }(), ...); +} + +void f8() { + t5<1, 2, 3>(); +} + +int references_enumerating() { + int x = 1, y = 2, z = 3; + template for (auto& v : {x, y, z}) { ++v; } + template for (auto&& v : {x, y, z}) { ++v; } + return x + y + z; +} + +// CHECK-LABEL: define {{.*}} void @_Z2f1v() +// CHECK: entry: +// CHECK-NEXT: %x = alloca i32, align 4 +// CHECK-NEXT: %x1 = alloca i32, align 4 +// CHECK-NEXT: %x3 = alloca i32, align 4 +// CHECK-NEXT: store i32 1, ptr %x, align 4 +// CHECK-NEXT: %0 = load i32, ptr %x, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %0) +// CHECK-NEXT: br label %expand.next +// CHECK: expand.next: +// CHECK-NEXT: store i32 2, ptr %x1, align 4 +// CHECK-NEXT: %1 = load i32, ptr %x1, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %1) +// CHECK-NEXT: br label %expand.next2 +// CHECK: expand.next2: +// CHECK-NEXT: store i32 3, ptr %x3, align 4 +// CHECK-NEXT: %2 = load i32, ptr %x3, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %2) +// CHECK-NEXT: br label %expand.end +// CHECK: expand.end: +// CHECK-NEXT: ret void + + +// CHECK-LABEL: define {{.*}} void @_Z2f2v() +// CHECK: entry: +// CHECK-NEXT: %x = alloca i32, align 4 +// CHECK-NEXT: %x1 = alloca ptr, align 8 +// CHECK-NEXT: %x3 = alloca %struct.S, align 4 +// CHECK-NEXT: %agg.tmp = alloca %struct.S, align 4 +// CHECK-NEXT: store i32 1, ptr %x, align 4 +// CHECK-NEXT: %0 = load i32, ptr %x, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %0) +// CHECK-NEXT: br label %expand.next +// CHECK: expand.next: +// CHECK-NEXT: store ptr @.str, ptr %x1, align 8 +// CHECK-NEXT: %1 = load ptr, ptr %x1, align 8 +// CHECK-NEXT: call void @_Z1gPKc(ptr {{.*}} %1) +// CHECK-NEXT: br label %expand.next2 +// CHECK: expand.next2: +// CHECK-NEXT: call void @_ZN1SC1Ei(ptr {{.*}} %x3, i32 {{.*}} 45) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %agg.tmp, ptr align 4 %x3, i64 4, i1 false) +// CHECK-NEXT: %coerce.dive = getelementptr inbounds nuw %struct.S, ptr %agg.tmp, i32 0, i32 0 +// CHECK-NEXT: %2 = load i32, ptr %coerce.dive, align 4 +// CHECK-NEXT: call void @_Z1g1S(i32 %2) +// CHECK-NEXT: br label %expand.end +// CHECK: expand.end: +// CHECK-NEXT: ret void + + +// CHECK-LABEL: define {{.*}} void @_Z2f3v() +// CHECK: entry: +// CHECK-NEXT: ret void + + +// CHECK-LABEL: define {{.*}} void @_Z2f4v() +// CHECK: entry: +// CHECK-NEXT: %x = alloca i32, align 4 +// CHECK-NEXT: %y = alloca i32, align 4 +// CHECK-NEXT: %y1 = alloca i32, align 4 +// CHECK-NEXT: %x3 = alloca i32, align 4 +// CHECK-NEXT: %y4 = alloca i32, align 4 +// CHECK-NEXT: %y6 = alloca i32, align 4 +// CHECK-NEXT: store i32 1, ptr %x, align 4 +// CHECK-NEXT: store i32 3, ptr %y, align 4 +// CHECK-NEXT: %0 = load i32, ptr %x, align 4 +// CHECK-NEXT: %1 = load i32, ptr %y, align 4 +// CHECK-NEXT: call void @_Z1hii(i32 {{.*}} %0, i32 {{.*}} %1) +// CHECK-NEXT: br label %expand.next +// CHECK: expand.next: +// CHECK-NEXT: store i32 4, ptr %y1, align 4 +// CHECK-NEXT: %2 = load i32, ptr %x, align 4 +// CHECK-NEXT: %3 = load i32, ptr %y1, align 4 +// CHECK-NEXT: call void @_Z1hii(i32 {{.*}} %2, i32 {{.*}} %3) +// CHECK-NEXT: br label %expand.end +// CHECK: expand.end: +// CHECK-NEXT: br label %expand.next2 +// CHECK: expand.next2: +// CHECK-NEXT: store i32 2, ptr %x3, align 4 +// CHECK-NEXT: store i32 3, ptr %y4, align 4 +// CHECK-NEXT: %4 = load i32, ptr %x3, align 4 +// CHECK-NEXT: %5 = load i32, ptr %y4, align 4 +// CHECK-NEXT: call void @_Z1hii(i32 {{.*}} %4, i32 {{.*}} %5) +// CHECK-NEXT: br label %expand.next5 +// CHECK: expand.next5: +// CHECK-NEXT: store i32 4, ptr %y6, align 4 +// CHECK-NEXT: %6 = load i32, ptr %x3, align 4 +// CHECK-NEXT: %7 = load i32, ptr %y6, align 4 +// CHECK-NEXT: call void @_Z1hii(i32 {{.*}} %6, i32 {{.*}} %7) +// CHECK-NEXT: br label %expand.end7 +// CHECK: expand.end7: +// CHECK-NEXT: br label %expand.end8 +// CHECK: expand.end8: +// CHECK-NEXT: ret void + + +// CHECK-LABEL: define {{.*}} void @_Z2f5v() +// CHECK: entry: +// CHECK-NEXT: %x = alloca i32, align 4 +// CHECK-NEXT: %x1 = alloca i32, align 4 +// CHECK-NEXT: %x2 = alloca i32, align 4 +// CHECK-NEXT: %x4 = alloca i32, align 4 +// CHECK-NEXT: %x6 = alloca i32, align 4 +// CHECK-NEXT: %x8 = alloca i32, align 4 +// CHECK-NEXT: %x10 = alloca i32, align 4 +// CHECK-NEXT: %x12 = alloca i32, align 4 +// CHECK-NEXT: %x14 = alloca i32, align 4 +// CHECK-NEXT: %x16 = alloca i32, align 4 +// CHECK-NEXT: %x18 = alloca i32, align 4 +// CHECK-NEXT: %x20 = alloca i32, align 4 +// CHECK-NEXT: %x22 = alloca i32, align 4 +// CHECK-NEXT: %x24 = alloca i32, align 4 +// CHECK-NEXT: %x26 = alloca i32, align 4 +// CHECK-NEXT: %x28 = alloca %struct.S, align 4 +// CHECK-NEXT: %x31 = alloca %struct.S, align 4 +// CHECK-NEXT: %x34 = alloca %struct.S, align 4 +// CHECK-NEXT: %x37 = alloca %struct.S, align 4 +// CHECK-NEXT: %x40 = alloca %struct.S, align 4 +// CHECK-NEXT: %x43 = alloca %struct.S, align 4 +// CHECK-NEXT: store i32 1, ptr %x, align 4 +// CHECK-NEXT: %0 = load i32, ptr %x, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %0) +// CHECK-NEXT: br label %expand.end +// CHECK: expand.end: +// CHECK-NEXT: store i32 2, ptr %x1, align 4 +// CHECK-NEXT: %1 = load i32, ptr %x1, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %1) +// CHECK-NEXT: br label %expand.next +// CHECK: expand.next: +// CHECK-NEXT: store i32 3, ptr %x2, align 4 +// CHECK-NEXT: %2 = load i32, ptr %x2, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %2) +// CHECK-NEXT: br label %expand.next3 +// CHECK: expand.next3: +// CHECK-NEXT: store i32 4, ptr %x4, align 4 +// CHECK-NEXT: %3 = load i32, ptr %x4, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %3) +// CHECK-NEXT: br label %expand.end5 +// CHECK: expand.end5: +// CHECK-NEXT: store i32 5, ptr %x6, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 5) +// CHECK-NEXT: br label %expand.end7 +// CHECK: expand.end7: +// CHECK-NEXT: store i32 6, ptr %x8, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 6) +// CHECK-NEXT: br label %expand.next9 +// CHECK: expand.next9: +// CHECK-NEXT: store i32 7, ptr %x10, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 7) +// CHECK-NEXT: br label %expand.next11 +// CHECK: expand.next11: +// CHECK-NEXT: store i32 8, ptr %x12, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 8) +// CHECK-NEXT: br label %expand.end13 +// CHECK: expand.end13: +// CHECK-NEXT: store i32 9, ptr %x14, align 4 +// CHECK-NEXT: %call = call {{.*}} i32 @_Z2tgILi9EEiv() +// CHECK-NEXT: br label %expand.end15 +// CHECK: expand.end15: +// CHECK-NEXT: store i32 10, ptr %x16, align 4 +// CHECK-NEXT: br label %expand.next17 +// CHECK: expand.next17: +// CHECK-NEXT: store i32 11, ptr %x18, align 4 +// CHECK-NEXT: br label %expand.next19 +// CHECK: expand.next19: +// CHECK-NEXT: store i32 12, ptr %x20, align 4 +// CHECK-NEXT: br label %expand.end21 +// CHECK: expand.end21: +// CHECK-NEXT: store i32 13, ptr %x22, align 4 +// CHECK-NEXT: %4 = load i32, ptr %x22, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %4) +// CHECK-NEXT: br label %expand.next23 +// CHECK: expand.next23: +// CHECK-NEXT: store i32 14, ptr %x24, align 4 +// CHECK-NEXT: %5 = load i32, ptr %x24, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %5) +// CHECK-NEXT: br label %expand.next25 +// CHECK: expand.next25: +// CHECK-NEXT: store i32 15, ptr %x26, align 4 +// CHECK-NEXT: %6 = load i32, ptr %x26, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %6) +// CHECK-NEXT: br label %expand.end27 +// CHECK: expand.end27: +// CHECK-NEXT: call void @_ZN1SC1Ei(ptr {{.*}} %x28, i32 {{.*}} 16) +// CHECK-NEXT: %x29 = getelementptr inbounds nuw %struct.S, ptr %x28, i32 0, i32 0 +// CHECK-NEXT: %7 = load i32, ptr %x29, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %7) +// CHECK-NEXT: br label %expand.next30 +// CHECK: expand.next30: +// CHECK-NEXT: call void @_ZN1SC1Ei(ptr {{.*}} %x31, i32 {{.*}} 17) +// CHECK-NEXT: %x32 = getelementptr inbounds nuw %struct.S, ptr %x31, i32 0, i32 0 +// CHECK-NEXT: %8 = load i32, ptr %x32, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %8) +// CHECK-NEXT: br label %expand.next33 +// CHECK: expand.next33: +// CHECK-NEXT: call void @_ZN1SC1Ei(ptr {{.*}} %x34, i32 {{.*}} 18) +// CHECK-NEXT: %x35 = getelementptr inbounds nuw %struct.S, ptr %x34, i32 0, i32 0 +// CHECK-NEXT: %9 = load i32, ptr %x35, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %9) +// CHECK-NEXT: br label %expand.end36 +// CHECK: expand.end36: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %x37, ptr align 4 @__const._Z2f5v.x, i64 4, i1 false) +// CHECK-NEXT: %call38 = call {{.*}} i32 @_Z2tgILi19EEiv() +// CHECK-NEXT: br label %expand.next39 +// CHECK: expand.next39: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %x40, ptr align 4 @__const._Z2f5v.x.1, i64 4, i1 false) +// CHECK-NEXT: %call41 = call {{.*}} i32 @_Z2tgILi20EEiv() +// CHECK-NEXT: br label %expand.next42 +// CHECK: expand.next42: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %x43, ptr align 4 @__const._Z2f5v.x.2, i64 4, i1 false) +// CHECK-NEXT: %call44 = call {{.*}} i32 @_Z2tgILi21EEiv() +// CHECK-NEXT: br label %expand.end45 +// CHECK: expand.end45: +// CHECK-NEXT: ret void + + +// CHECK-LABEL: define {{.*}} void @_Z2f6v() +// CHECK: entry: +// CHECK-NEXT: %ref.tmp = alloca %struct.s1, align 1 +// CHECK-NEXT: %ref.tmp1 = alloca %struct.s1.0, align 1 +// CHECK-NEXT: %ref.tmp2 = alloca %struct.s1.0, align 1 +// CHECK-NEXT: %ref.tmp3 = alloca %struct.s1, align 1 +// CHECK-NEXT: call void @_Z2t1IiEvv() +// CHECK-NEXT: call void @_Z2t1IlEvv() +// CHECK-NEXT: call void @_ZN2s1IlE2tfIlEEvv(ptr {{.*}} %ref.tmp) +// CHECK-NEXT: call void @_ZN2s1IiE2tfIiEEvv(ptr {{.*}} %ref.tmp1) +// CHECK-NEXT: call void @_ZN2s1IiE2tfIlEEvv(ptr {{.*}} %ref.tmp2) +// CHECK-NEXT: call void @_ZN2s1IlE2tfIiEEvv(ptr {{.*}} %ref.tmp3) +// CHECK-NEXT: call void @_Z2t2I1SEvv() +// CHECK-NEXT: call void @_Z2t2IA1231_1SEvv() +// CHECK-NEXT: call void @_Z2t2IPPP1SEvv() +// CHECK-NEXT: ret void + + +// CHECK-LABEL: define {{.*}} void @_Z2t1IiEvv() +// CHECK: entry: +// CHECK-NEXT: %x = alloca i32, align 4 +// CHECK-NEXT: %x1 = alloca i32, align 4 +// CHECK-NEXT: %x2 = alloca i32, align 4 +// CHECK-NEXT: %x4 = alloca i32, align 4 +// CHECK-NEXT: %x6 = alloca i32, align 4 +// CHECK-NEXT: %x8 = alloca i32, align 4 +// CHECK-NEXT: %x10 = alloca i32, align 4 +// CHECK-NEXT: %x12 = alloca i32, align 4 +// CHECK-NEXT: %x14 = alloca i32, align 4 +// CHECK-NEXT: %x16 = alloca i32, align 4 +// CHECK-NEXT: store i32 1, ptr %x, align 4 +// CHECK-NEXT: %0 = load i32, ptr %x, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %0) +// CHECK-NEXT: br label %expand.next +// CHECK: expand.next: +// CHECK-NEXT: store i32 2, ptr %x1, align 4 +// CHECK-NEXT: %1 = load i32, ptr %x1, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %1) +// CHECK-NEXT: br label %expand.end +// CHECK: expand.end: +// CHECK-NEXT: store i32 3, ptr %x2, align 4 +// CHECK-NEXT: %2 = load i32, ptr %x2, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %2) +// CHECK-NEXT: br label %expand.next3 +// CHECK: expand.next3: +// CHECK-NEXT: store i32 4, ptr %x4, align 4 +// CHECK-NEXT: %3 = load i32, ptr %x4, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %3) +// CHECK-NEXT: br label %expand.end5 +// CHECK: expand.end5: +// CHECK-NEXT: store i32 5, ptr %x6, align 4 +// CHECK-NEXT: %4 = load i32, ptr %x6, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %4) +// CHECK-NEXT: br label %expand.next7 +// CHECK: expand.next7: +// CHECK-NEXT: store i32 6, ptr %x8, align 4 +// CHECK-NEXT: %5 = load i32, ptr %x8, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %5) +// CHECK-NEXT: br label %expand.end9 +// CHECK: expand.end9: +// CHECK-NEXT: store i32 7, ptr %x10, align 4 +// CHECK-NEXT: br label %expand.next11 +// CHECK: expand.next11: +// CHECK-NEXT: store i32 8, ptr %x12, align 4 +// CHECK-NEXT: br label %expand.end13 +// CHECK: expand.end13: +// CHECK-NEXT: store i32 9, ptr %x14, align 4 +// CHECK-NEXT: br label %expand.next15 +// CHECK: expand.next15: +// CHECK-NEXT: store i32 10, ptr %x16, align 4 +// CHECK-NEXT: br label %expand.end17 +// CHECK: expand.end17: +// CHECK-NEXT: ret void + + +// CHECK-LABEL: define {{.*}} void @_Z2t1IlEvv() +// CHECK: entry: +// CHECK-NEXT: %x = alloca i64, align 8 +// CHECK-NEXT: %x1 = alloca i64, align 8 +// CHECK-NEXT: %x2 = alloca i64, align 8 +// CHECK-NEXT: %x4 = alloca i64, align 8 +// CHECK-NEXT: %x6 = alloca i64, align 8 +// CHECK-NEXT: %x8 = alloca i64, align 8 +// CHECK-NEXT: %x10 = alloca i64, align 8 +// CHECK-NEXT: %x12 = alloca i64, align 8 +// CHECK-NEXT: %x14 = alloca i64, align 8 +// CHECK-NEXT: %x16 = alloca i64, align 8 +// CHECK-NEXT: store i64 1, ptr %x, align 8 +// CHECK-NEXT: %0 = load i64, ptr %x, align 8 +// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %0) +// CHECK-NEXT: br label %expand.next +// CHECK: expand.next: +// CHECK-NEXT: store i64 2, ptr %x1, align 8 +// CHECK-NEXT: %1 = load i64, ptr %x1, align 8 +// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %1) +// CHECK-NEXT: br label %expand.end +// CHECK: expand.end: +// CHECK-NEXT: store i64 3, ptr %x2, align 8 +// CHECK-NEXT: %2 = load i64, ptr %x2, align 8 +// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %2) +// CHECK-NEXT: br label %expand.next3 +// CHECK: expand.next3: +// CHECK-NEXT: store i64 4, ptr %x4, align 8 +// CHECK-NEXT: %3 = load i64, ptr %x4, align 8 +// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %3) +// CHECK-NEXT: br label %expand.end5 +// CHECK: expand.end5: +// CHECK-NEXT: store i64 5, ptr %x6, align 8 +// CHECK-NEXT: %4 = load i64, ptr %x6, align 8 +// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %4) +// CHECK-NEXT: br label %expand.next7 +// CHECK: expand.next7: +// CHECK-NEXT: store i64 6, ptr %x8, align 8 +// CHECK-NEXT: %5 = load i64, ptr %x8, align 8 +// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %5) +// CHECK-NEXT: br label %expand.end9 +// CHECK: expand.end9: +// CHECK-NEXT: store i64 7, ptr %x10, align 8 +// CHECK-NEXT: br label %expand.next11 +// CHECK: expand.next11: +// CHECK-NEXT: store i64 8, ptr %x12, align 8 +// CHECK-NEXT: br label %expand.end13 +// CHECK: expand.end13: +// CHECK-NEXT: store i64 9, ptr %x14, align 8 +// CHECK-NEXT: br label %expand.next15 +// CHECK: expand.next15: +// CHECK-NEXT: store i64 10, ptr %x16, align 8 +// CHECK-NEXT: br label %expand.end17 +// CHECK: expand.end17: +// CHECK-NEXT: ret void + + +// CHECK-LABEL: define {{.*}} void @_ZN2s1IlE2tfIlEEvv(ptr {{.*}} %this) +// CHECK: entry: +// CHECK-NEXT: %this.addr = alloca ptr, align 8 +// CHECK-NEXT: %x = alloca i64, align 8 +// CHECK-NEXT: %x2 = alloca i64, align 8 +// CHECK-NEXT: %x3 = alloca i64, align 8 +// CHECK-NEXT: %x5 = alloca i64, align 8 +// CHECK-NEXT: %x7 = alloca i64, align 8 +// CHECK-NEXT: %x9 = alloca i64, align 8 +// CHECK-NEXT: %x11 = alloca i64, align 8 +// CHECK-NEXT: %x13 = alloca i64, align 8 +// CHECK-NEXT: %x15 = alloca i64, align 8 +// CHECK-NEXT: %x17 = alloca i64, align 8 +// CHECK-NEXT: %x19 = alloca i64, align 8 +// CHECK-NEXT: %x21 = alloca i64, align 8 +// CHECK-NEXT: %x23 = alloca i64, align 8 +// CHECK-NEXT: %x25 = alloca i64, align 8 +// CHECK-NEXT: %x27 = alloca i64, align 8 +// CHECK-NEXT: %x29 = alloca i64, align 8 +// CHECK-NEXT: %x31 = alloca i64, align 8 +// CHECK-NEXT: %x33 = alloca i64, align 8 +// CHECK-NEXT: store ptr %this, ptr %this.addr, align 8 +// CHECK-NEXT: %this1 = load ptr, ptr %this.addr, align 8 +// CHECK-NEXT: store i64 1, ptr %x, align 8 +// CHECK-NEXT: %0 = load i64, ptr %x, align 8 +// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %0) +// CHECK-NEXT: br label %expand.next +// CHECK: expand.next: +// CHECK-NEXT: store i64 2, ptr %x2, align 8 +// CHECK-NEXT: %1 = load i64, ptr %x2, align 8 +// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %1) +// CHECK-NEXT: br label %expand.end +// CHECK: expand.end: +// CHECK-NEXT: store i64 3, ptr %x3, align 8 +// CHECK-NEXT: %2 = load i64, ptr %x3, align 8 +// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %2) +// CHECK-NEXT: br label %expand.next4 +// CHECK: expand.next4: +// CHECK-NEXT: store i64 4, ptr %x5, align 8 +// CHECK-NEXT: %3 = load i64, ptr %x5, align 8 +// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %3) +// CHECK-NEXT: br label %expand.end6 +// CHECK: expand.end6: +// CHECK-NEXT: store i64 5, ptr %x7, align 8 +// CHECK-NEXT: %4 = load i64, ptr %x7, align 8 +// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %4) +// CHECK-NEXT: br label %expand.next8 +// CHECK: expand.next8: +// CHECK-NEXT: store i64 6, ptr %x9, align 8 +// CHECK-NEXT: %5 = load i64, ptr %x9, align 8 +// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %5) +// CHECK-NEXT: br label %expand.end10 +// CHECK: expand.end10: +// CHECK-NEXT: store i64 7, ptr %x11, align 8 +// CHECK-NEXT: %6 = load i64, ptr %x11, align 8 +// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %6) +// CHECK-NEXT: br label %expand.next12 +// CHECK: expand.next12: +// CHECK-NEXT: store i64 8, ptr %x13, align 8 +// CHECK-NEXT: %7 = load i64, ptr %x13, align 8 +// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %7) +// CHECK-NEXT: br label %expand.end14 +// CHECK: expand.end14: +// CHECK-NEXT: store i64 9, ptr %x15, align 8 +// CHECK-NEXT: %8 = load i64, ptr %x15, align 8 +// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %8) +// CHECK-NEXT: br label %expand.next16 +// CHECK: expand.next16: +// CHECK-NEXT: store i64 10, ptr %x17, align 8 +// CHECK-NEXT: %9 = load i64, ptr %x17, align 8 +// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %9) +// CHECK-NEXT: br label %expand.end18 +// CHECK: expand.end18: +// CHECK-NEXT: store i64 11, ptr %x19, align 8 +// CHECK-NEXT: %10 = load i64, ptr %x19, align 8 +// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %10) +// CHECK-NEXT: br label %expand.next20 +// CHECK: expand.next20: +// CHECK-NEXT: store i64 12, ptr %x21, align 8 +// CHECK-NEXT: %11 = load i64, ptr %x21, align 8 +// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %11) +// CHECK-NEXT: br label %expand.end22 +// CHECK: expand.end22: +// CHECK-NEXT: store i64 13, ptr %x23, align 8 +// CHECK-NEXT: br label %expand.next24 +// CHECK: expand.next24: +// CHECK-NEXT: store i64 14, ptr %x25, align 8 +// CHECK-NEXT: br label %expand.end26 +// CHECK: expand.end26: +// CHECK-NEXT: store i64 15, ptr %x27, align 8 +// CHECK-NEXT: br label %expand.next28 +// CHECK: expand.next28: +// CHECK-NEXT: store i64 16, ptr %x29, align 8 +// CHECK-NEXT: br label %expand.end30 +// CHECK: expand.end30: +// CHECK-NEXT: store i64 17, ptr %x31, align 8 +// CHECK-NEXT: br label %expand.next32 +// CHECK: expand.next32: +// CHECK-NEXT: store i64 18, ptr %x33, align 8 +// CHECK-NEXT: br label %expand.end34 +// CHECK: expand.end34: +// CHECK-NEXT: ret void + + +// CHECK-LABEL: define {{.*}} void @_ZN2s1IiE2tfIiEEvv(ptr {{.*}} %this) +// CHECK: entry: +// CHECK-NEXT: %this.addr = alloca ptr, align 8 +// CHECK-NEXT: %x = alloca i32, align 4 +// CHECK-NEXT: %x2 = alloca i32, align 4 +// CHECK-NEXT: %x3 = alloca i32, align 4 +// CHECK-NEXT: %x5 = alloca i32, align 4 +// CHECK-NEXT: %x7 = alloca i32, align 4 +// CHECK-NEXT: %x9 = alloca i32, align 4 +// CHECK-NEXT: %x11 = alloca i32, align 4 +// CHECK-NEXT: %x13 = alloca i32, align 4 +// CHECK-NEXT: %x15 = alloca i32, align 4 +// CHECK-NEXT: %x17 = alloca i32, align 4 +// CHECK-NEXT: %x19 = alloca i32, align 4 +// CHECK-NEXT: %x21 = alloca i32, align 4 +// CHECK-NEXT: %x23 = alloca i32, align 4 +// CHECK-NEXT: %x25 = alloca i32, align 4 +// CHECK-NEXT: %x27 = alloca i32, align 4 +// CHECK-NEXT: %x29 = alloca i32, align 4 +// CHECK-NEXT: %x31 = alloca i32, align 4 +// CHECK-NEXT: %x33 = alloca i32, align 4 +// CHECK-NEXT: store ptr %this, ptr %this.addr, align 8 +// CHECK-NEXT: %this1 = load ptr, ptr %this.addr, align 8 +// CHECK-NEXT: store i32 1, ptr %x, align 4 +// CHECK-NEXT: %0 = load i32, ptr %x, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %0) +// CHECK-NEXT: br label %expand.next +// CHECK: expand.next: +// CHECK-NEXT: store i32 2, ptr %x2, align 4 +// CHECK-NEXT: %1 = load i32, ptr %x2, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %1) +// CHECK-NEXT: br label %expand.end +// CHECK: expand.end: +// CHECK-NEXT: store i32 3, ptr %x3, align 4 +// CHECK-NEXT: %2 = load i32, ptr %x3, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %2) +// CHECK-NEXT: br label %expand.next4 +// CHECK: expand.next4: +// CHECK-NEXT: store i32 4, ptr %x5, align 4 +// CHECK-NEXT: %3 = load i32, ptr %x5, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %3) +// CHECK-NEXT: br label %expand.end6 +// CHECK: expand.end6: +// CHECK-NEXT: store i32 5, ptr %x7, align 4 +// CHECK-NEXT: %4 = load i32, ptr %x7, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %4) +// CHECK-NEXT: br label %expand.next8 +// CHECK: expand.next8: +// CHECK-NEXT: store i32 6, ptr %x9, align 4 +// CHECK-NEXT: %5 = load i32, ptr %x9, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %5) +// CHECK-NEXT: br label %expand.end10 +// CHECK: expand.end10: +// CHECK-NEXT: store i32 7, ptr %x11, align 4 +// CHECK-NEXT: %6 = load i32, ptr %x11, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %6) +// CHECK-NEXT: br label %expand.next12 +// CHECK: expand.next12: +// CHECK-NEXT: store i32 8, ptr %x13, align 4 +// CHECK-NEXT: %7 = load i32, ptr %x13, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %7) +// CHECK-NEXT: br label %expand.end14 +// CHECK: expand.end14: +// CHECK-NEXT: store i32 9, ptr %x15, align 4 +// CHECK-NEXT: %8 = load i32, ptr %x15, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %8) +// CHECK-NEXT: br label %expand.next16 +// CHECK: expand.next16: +// CHECK-NEXT: store i32 10, ptr %x17, align 4 +// CHECK-NEXT: %9 = load i32, ptr %x17, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %9) +// CHECK-NEXT: br label %expand.end18 +// CHECK: expand.end18: +// CHECK-NEXT: store i32 11, ptr %x19, align 4 +// CHECK-NEXT: %10 = load i32, ptr %x19, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %10) +// CHECK-NEXT: br label %expand.next20 +// CHECK: expand.next20: +// CHECK-NEXT: store i32 12, ptr %x21, align 4 +// CHECK-NEXT: %11 = load i32, ptr %x21, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %11) +// CHECK-NEXT: br label %expand.end22 +// CHECK: expand.end22: +// CHECK-NEXT: store i32 13, ptr %x23, align 4 +// CHECK-NEXT: br label %expand.next24 +// CHECK: expand.next24: +// CHECK-NEXT: store i32 14, ptr %x25, align 4 +// CHECK-NEXT: br label %expand.end26 +// CHECK: expand.end26: +// CHECK-NEXT: store i32 15, ptr %x27, align 4 +// CHECK-NEXT: br label %expand.next28 +// CHECK: expand.next28: +// CHECK-NEXT: store i32 16, ptr %x29, align 4 +// CHECK-NEXT: br label %expand.end30 +// CHECK: expand.end30: +// CHECK-NEXT: store i32 17, ptr %x31, align 4 +// CHECK-NEXT: br label %expand.next32 +// CHECK: expand.next32: +// CHECK-NEXT: store i32 18, ptr %x33, align 4 +// CHECK-NEXT: br label %expand.end34 +// CHECK: expand.end34: +// CHECK-NEXT: ret void + + +// CHECK-LABEL: define {{.*}} void @_ZN2s1IiE2tfIlEEvv(ptr {{.*}} %this) +// CHECK: entry: +// CHECK-NEXT: %this.addr = alloca ptr, align 8 +// CHECK-NEXT: %x = alloca i64, align 8 +// CHECK-NEXT: %x2 = alloca i64, align 8 +// CHECK-NEXT: %x3 = alloca i32, align 4 +// CHECK-NEXT: %x5 = alloca i32, align 4 +// CHECK-NEXT: %x7 = alloca i32, align 4 +// CHECK-NEXT: %x9 = alloca i32, align 4 +// CHECK-NEXT: %x11 = alloca i64, align 8 +// CHECK-NEXT: %x13 = alloca i64, align 8 +// CHECK-NEXT: %x15 = alloca i64, align 8 +// CHECK-NEXT: %x17 = alloca i64, align 8 +// CHECK-NEXT: %x19 = alloca i32, align 4 +// CHECK-NEXT: %x21 = alloca i64, align 8 +// CHECK-NEXT: %x23 = alloca i32, align 4 +// CHECK-NEXT: %x25 = alloca i32, align 4 +// CHECK-NEXT: %x27 = alloca i64, align 8 +// CHECK-NEXT: %x29 = alloca i64, align 8 +// CHECK-NEXT: %x31 = alloca i64, align 8 +// CHECK-NEXT: %x33 = alloca i32, align 4 +// CHECK-NEXT: store ptr %this, ptr %this.addr, align 8 +// CHECK-NEXT: %this1 = load ptr, ptr %this.addr, align 8 +// CHECK-NEXT: store i64 1, ptr %x, align 8 +// CHECK-NEXT: %0 = load i64, ptr %x, align 8 +// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %0) +// CHECK-NEXT: br label %expand.next +// CHECK: expand.next: +// CHECK-NEXT: store i64 2, ptr %x2, align 8 +// CHECK-NEXT: %1 = load i64, ptr %x2, align 8 +// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %1) +// CHECK-NEXT: br label %expand.end +// CHECK: expand.end: +// CHECK-NEXT: store i32 3, ptr %x3, align 4 +// CHECK-NEXT: %2 = load i32, ptr %x3, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %2) +// CHECK-NEXT: br label %expand.next4 +// CHECK: expand.next4: +// CHECK-NEXT: store i32 4, ptr %x5, align 4 +// CHECK-NEXT: %3 = load i32, ptr %x5, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %3) +// CHECK-NEXT: br label %expand.end6 +// CHECK: expand.end6: +// CHECK-NEXT: store i32 5, ptr %x7, align 4 +// CHECK-NEXT: %4 = load i32, ptr %x7, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %4) +// CHECK-NEXT: br label %expand.next8 +// CHECK: expand.next8: +// CHECK-NEXT: store i32 6, ptr %x9, align 4 +// CHECK-NEXT: %5 = load i32, ptr %x9, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %5) +// CHECK-NEXT: br label %expand.end10 +// CHECK: expand.end10: +// CHECK-NEXT: store i64 7, ptr %x11, align 8 +// CHECK-NEXT: %6 = load i64, ptr %x11, align 8 +// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %6) +// CHECK-NEXT: br label %expand.next12 +// CHECK: expand.next12: +// CHECK-NEXT: store i64 8, ptr %x13, align 8 +// CHECK-NEXT: %7 = load i64, ptr %x13, align 8 +// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %7) +// CHECK-NEXT: br label %expand.end14 +// CHECK: expand.end14: +// CHECK-NEXT: store i64 9, ptr %x15, align 8 +// CHECK-NEXT: %8 = load i64, ptr %x15, align 8 +// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %8) +// CHECK-NEXT: br label %expand.next16 +// CHECK: expand.next16: +// CHECK-NEXT: store i64 10, ptr %x17, align 8 +// CHECK-NEXT: %9 = load i64, ptr %x17, align 8 +// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %9) +// CHECK-NEXT: br label %expand.end18 +// CHECK: expand.end18: +// CHECK-NEXT: store i32 11, ptr %x19, align 4 +// CHECK-NEXT: %10 = load i32, ptr %x19, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %10) +// CHECK-NEXT: br label %expand.next20 +// CHECK: expand.next20: +// CHECK-NEXT: store i64 12, ptr %x21, align 8 +// CHECK-NEXT: %11 = load i64, ptr %x21, align 8 +// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %11) +// CHECK-NEXT: br label %expand.end22 +// CHECK: expand.end22: +// CHECK-NEXT: store i32 13, ptr %x23, align 4 +// CHECK-NEXT: br label %expand.next24 +// CHECK: expand.next24: +// CHECK-NEXT: store i32 14, ptr %x25, align 4 +// CHECK-NEXT: br label %expand.end26 +// CHECK: expand.end26: +// CHECK-NEXT: store i64 15, ptr %x27, align 8 +// CHECK-NEXT: br label %expand.next28 +// CHECK: expand.next28: +// CHECK-NEXT: store i64 16, ptr %x29, align 8 +// CHECK-NEXT: br label %expand.end30 +// CHECK: expand.end30: +// CHECK-NEXT: store i64 17, ptr %x31, align 8 +// CHECK-NEXT: br label %expand.next32 +// CHECK: expand.next32: +// CHECK-NEXT: store i32 18, ptr %x33, align 4 +// CHECK-NEXT: br label %expand.end34 +// CHECK: expand.end34: +// CHECK-NEXT: ret void + + +// CHECK-LABEL: define {{.*}} void @_ZN2s1IlE2tfIiEEvv(ptr {{.*}} %this) +// CHECK: entry: +// CHECK-NEXT: %this.addr = alloca ptr, align 8 +// CHECK-NEXT: %x = alloca i32, align 4 +// CHECK-NEXT: %x2 = alloca i32, align 4 +// CHECK-NEXT: %x3 = alloca i64, align 8 +// CHECK-NEXT: %x5 = alloca i64, align 8 +// CHECK-NEXT: %x7 = alloca i64, align 8 +// CHECK-NEXT: %x9 = alloca i64, align 8 +// CHECK-NEXT: %x11 = alloca i32, align 4 +// CHECK-NEXT: %x13 = alloca i32, align 4 +// CHECK-NEXT: %x15 = alloca i32, align 4 +// CHECK-NEXT: %x17 = alloca i32, align 4 +// CHECK-NEXT: %x19 = alloca i64, align 8 +// CHECK-NEXT: %x21 = alloca i32, align 4 +// CHECK-NEXT: %x23 = alloca i64, align 8 +// CHECK-NEXT: %x25 = alloca i64, align 8 +// CHECK-NEXT: %x27 = alloca i32, align 4 +// CHECK-NEXT: %x29 = alloca i32, align 4 +// CHECK-NEXT: %x31 = alloca i32, align 4 +// CHECK-NEXT: %x33 = alloca i64, align 8 +// CHECK-NEXT: store ptr %this, ptr %this.addr, align 8 +// CHECK-NEXT: %this1 = load ptr, ptr %this.addr, align 8 +// CHECK-NEXT: store i32 1, ptr %x, align 4 +// CHECK-NEXT: %0 = load i32, ptr %x, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %0) +// CHECK-NEXT: br label %expand.next +// CHECK: expand.next: +// CHECK-NEXT: store i32 2, ptr %x2, align 4 +// CHECK-NEXT: %1 = load i32, ptr %x2, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %1) +// CHECK-NEXT: br label %expand.end +// CHECK: expand.end: +// CHECK-NEXT: store i64 3, ptr %x3, align 8 +// CHECK-NEXT: %2 = load i64, ptr %x3, align 8 +// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %2) +// CHECK-NEXT: br label %expand.next4 +// CHECK: expand.next4: +// CHECK-NEXT: store i64 4, ptr %x5, align 8 +// CHECK-NEXT: %3 = load i64, ptr %x5, align 8 +// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %3) +// CHECK-NEXT: br label %expand.end6 +// CHECK: expand.end6: +// CHECK-NEXT: store i64 5, ptr %x7, align 8 +// CHECK-NEXT: %4 = load i64, ptr %x7, align 8 +// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %4) +// CHECK-NEXT: br label %expand.next8 +// CHECK: expand.next8: +// CHECK-NEXT: store i64 6, ptr %x9, align 8 +// CHECK-NEXT: %5 = load i64, ptr %x9, align 8 +// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %5) +// CHECK-NEXT: br label %expand.end10 +// CHECK: expand.end10: +// CHECK-NEXT: store i32 7, ptr %x11, align 4 +// CHECK-NEXT: %6 = load i32, ptr %x11, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %6) +// CHECK-NEXT: br label %expand.next12 +// CHECK: expand.next12: +// CHECK-NEXT: store i32 8, ptr %x13, align 4 +// CHECK-NEXT: %7 = load i32, ptr %x13, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %7) +// CHECK-NEXT: br label %expand.end14 +// CHECK: expand.end14: +// CHECK-NEXT: store i32 9, ptr %x15, align 4 +// CHECK-NEXT: %8 = load i32, ptr %x15, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %8) +// CHECK-NEXT: br label %expand.next16 +// CHECK: expand.next16: +// CHECK-NEXT: store i32 10, ptr %x17, align 4 +// CHECK-NEXT: %9 = load i32, ptr %x17, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %9) +// CHECK-NEXT: br label %expand.end18 +// CHECK: expand.end18: +// CHECK-NEXT: store i64 11, ptr %x19, align 8 +// CHECK-NEXT: %10 = load i64, ptr %x19, align 8 +// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %10) +// CHECK-NEXT: br label %expand.next20 +// CHECK: expand.next20: +// CHECK-NEXT: store i32 12, ptr %x21, align 4 +// CHECK-NEXT: %11 = load i32, ptr %x21, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %11) +// CHECK-NEXT: br label %expand.end22 +// CHECK: expand.end22: +// CHECK-NEXT: store i64 13, ptr %x23, align 8 +// CHECK-NEXT: br label %expand.next24 +// CHECK: expand.next24: +// CHECK-NEXT: store i64 14, ptr %x25, align 8 +// CHECK-NEXT: br label %expand.end26 +// CHECK: expand.end26: +// CHECK-NEXT: store i32 15, ptr %x27, align 4 +// CHECK-NEXT: br label %expand.next28 +// CHECK: expand.next28: +// CHECK-NEXT: store i32 16, ptr %x29, align 4 +// CHECK-NEXT: br label %expand.end30 +// CHECK: expand.end30: +// CHECK-NEXT: store i32 17, ptr %x31, align 4 +// CHECK-NEXT: br label %expand.next32 +// CHECK: expand.next32: +// CHECK-NEXT: store i64 18, ptr %x33, align 8 +// CHECK-NEXT: br label %expand.end34 +// CHECK: expand.end34: +// CHECK-NEXT: ret void + + +// CHECK-LABEL: define {{.*}} void @_Z2f7v() +// CHECK: entry: +// CHECK-NEXT: %ref.tmp = alloca %struct.s2, align 1 +// CHECK-NEXT: call void @_Z2t3IJiiiEEvDpT_(i32 {{.*}} 42, i32 {{.*}} 43, i32 {{.*}} 44) +// CHECK-NEXT: call void @_Z2t4IJLi42ELi43ELi44EEEvv() +// CHECK-NEXT: call void @_ZN2s2IJLi1ELi2ELi3EEE2tfIJLi4ELi5ELi6EEEEvv(ptr {{.*}} %ref.tmp) +// CHECK-NEXT: ret void + +// CHECK-LABEL: define {{.*}} void @_Z2t3IJiiiEEvDpT_(i32 {{.*}} %ts, i32 {{.*}} %ts1, i32 {{.*}} %ts3) +// CHECK: entry: +// CHECK-NEXT: %ts.addr = alloca i32, align 4 +// CHECK-NEXT: %ts.addr2 = alloca i32, align 4 +// CHECK-NEXT: %ts.addr4 = alloca i32, align 4 +// CHECK-NEXT: %x = alloca i32, align 4 +// CHECK-NEXT: %x5 = alloca i32, align 4 +// CHECK-NEXT: %x7 = alloca i32, align 4 +// CHECK-NEXT: %x8 = alloca i32, align 4 +// CHECK-NEXT: %x10 = alloca i32, align 4 +// CHECK-NEXT: %x12 = alloca i32, align 4 +// CHECK-NEXT: %x14 = alloca i32, align 4 +// CHECK-NEXT: %x16 = alloca i32, align 4 +// CHECK-NEXT: %x18 = alloca i32, align 4 +// CHECK-NEXT: %x20 = alloca i32, align 4 +// CHECK-NEXT: %x22 = alloca i32, align 4 +// CHECK-NEXT: %x24 = alloca i32, align 4 +// CHECK-NEXT: %x26 = alloca i32, align 4 +// CHECK-NEXT: %x28 = alloca i32, align 4 +// CHECK-NEXT: %x30 = alloca i32, align 4 +// CHECK-NEXT: %x32 = alloca i32, align 4 +// CHECK-NEXT: %x34 = alloca i32, align 4 +// CHECK-NEXT: %x36 = alloca i32, align 4 +// CHECK-NEXT: %x38 = alloca i32, align 4 +// CHECK-NEXT: %x40 = alloca i32, align 4 +// CHECK-NEXT: %x42 = alloca %struct.X, align 4 +// CHECK-NEXT: %x45 = alloca %struct.X, align 4 +// CHECK-NEXT: %x51 = alloca %struct.X, align 4 +// CHECK-NEXT: %x54 = alloca %struct.X, align 4 +// CHECK-NEXT: store i32 %ts, ptr %ts.addr, align 4 +// CHECK-NEXT: store i32 %ts1, ptr %ts.addr2, align 4 +// CHECK-NEXT: store i32 %ts3, ptr %ts.addr4, align 4 +// CHECK-NEXT: %0 = load i32, ptr %ts.addr, align 4 +// CHECK-NEXT: store i32 %0, ptr %x, align 4 +// CHECK-NEXT: %1 = load i32, ptr %x, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %1) +// CHECK-NEXT: br label %expand.next +// CHECK: expand.next: +// CHECK-NEXT: %2 = load i32, ptr %ts.addr2, align 4 +// CHECK-NEXT: store i32 %2, ptr %x5, align 4 +// CHECK-NEXT: %3 = load i32, ptr %x5, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %3) +// CHECK-NEXT: br label %expand.next6 +// CHECK: expand.next6: +// CHECK-NEXT: %4 = load i32, ptr %ts.addr4, align 4 +// CHECK-NEXT: store i32 %4, ptr %x7, align 4 +// CHECK-NEXT: %5 = load i32, ptr %x7, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %5) +// CHECK-NEXT: br label %expand.end +// CHECK: expand.end: +// CHECK-NEXT: store i32 1, ptr %x8, align 4 +// CHECK-NEXT: %6 = load i32, ptr %x8, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %6) +// CHECK-NEXT: br label %expand.next9 +// CHECK: expand.next9: +// CHECK-NEXT: %7 = load i32, ptr %ts.addr, align 4 +// CHECK-NEXT: store i32 %7, ptr %x10, align 4 +// CHECK-NEXT: %8 = load i32, ptr %x10, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %8) +// CHECK-NEXT: br label %expand.next11 +// CHECK: expand.next11: +// CHECK-NEXT: %9 = load i32, ptr %ts.addr2, align 4 +// CHECK-NEXT: store i32 %9, ptr %x12, align 4 +// CHECK-NEXT: %10 = load i32, ptr %x12, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %10) +// CHECK-NEXT: br label %expand.next13 +// CHECK: expand.next13: +// CHECK-NEXT: %11 = load i32, ptr %ts.addr4, align 4 +// CHECK-NEXT: store i32 %11, ptr %x14, align 4 +// CHECK-NEXT: %12 = load i32, ptr %x14, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %12) +// CHECK-NEXT: br label %expand.next15 +// CHECK: expand.next15: +// CHECK-NEXT: store i32 2, ptr %x16, align 4 +// CHECK-NEXT: %13 = load i32, ptr %x16, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %13) +// CHECK-NEXT: br label %expand.next17 +// CHECK: expand.next17: +// CHECK-NEXT: %14 = load i32, ptr %ts.addr, align 4 +// CHECK-NEXT: store i32 %14, ptr %x18, align 4 +// CHECK-NEXT: %15 = load i32, ptr %x18, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %15) +// CHECK-NEXT: br label %expand.next19 +// CHECK: expand.next19: +// CHECK-NEXT: %16 = load i32, ptr %ts.addr2, align 4 +// CHECK-NEXT: store i32 %16, ptr %x20, align 4 +// CHECK-NEXT: %17 = load i32, ptr %x20, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %17) +// CHECK-NEXT: br label %expand.next21 +// CHECK: expand.next21: +// CHECK-NEXT: %18 = load i32, ptr %ts.addr4, align 4 +// CHECK-NEXT: store i32 %18, ptr %x22, align 4 +// CHECK-NEXT: %19 = load i32, ptr %x22, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %19) +// CHECK-NEXT: br label %expand.next23 +// CHECK: expand.next23: +// CHECK-NEXT: store i32 3, ptr %x24, align 4 +// CHECK-NEXT: %20 = load i32, ptr %x24, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %20) +// CHECK-NEXT: br label %expand.end25 +// CHECK: expand.end25: +// CHECK-NEXT: store i32 4, ptr %x26, align 4 +// CHECK-NEXT: %21 = load i32, ptr %x26, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %21) +// CHECK-NEXT: br label %expand.next27 +// CHECK: expand.next27: +// CHECK-NEXT: %22 = load i32, ptr %ts.addr, align 4 +// CHECK-NEXT: store i32 %22, ptr %x28, align 4 +// CHECK-NEXT: %23 = load i32, ptr %x28, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %23) +// CHECK-NEXT: br label %expand.next29 +// CHECK: expand.next29: +// CHECK-NEXT: %24 = load i32, ptr %ts.addr2, align 4 +// CHECK-NEXT: store i32 %24, ptr %x30, align 4 +// CHECK-NEXT: %25 = load i32, ptr %x30, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %25) +// CHECK-NEXT: br label %expand.next31 +// CHECK: expand.next31: +// CHECK-NEXT: %26 = load i32, ptr %ts.addr4, align 4 +// CHECK-NEXT: store i32 %26, ptr %x32, align 4 +// CHECK-NEXT: %27 = load i32, ptr %x32, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %27) +// CHECK-NEXT: br label %expand.next33 +// CHECK: expand.next33: +// CHECK-NEXT: %28 = load i32, ptr %ts.addr, align 4 +// CHECK-NEXT: store i32 %28, ptr %x34, align 4 +// CHECK-NEXT: %29 = load i32, ptr %x34, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %29) +// CHECK-NEXT: br label %expand.next35 +// CHECK: expand.next35: +// CHECK-NEXT: %30 = load i32, ptr %ts.addr2, align 4 +// CHECK-NEXT: store i32 %30, ptr %x36, align 4 +// CHECK-NEXT: %31 = load i32, ptr %x36, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %31) +// CHECK-NEXT: br label %expand.next37 +// CHECK: expand.next37: +// CHECK-NEXT: %32 = load i32, ptr %ts.addr4, align 4 +// CHECK-NEXT: store i32 %32, ptr %x38, align 4 +// CHECK-NEXT: %33 = load i32, ptr %x38, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %33) +// CHECK-NEXT: br label %expand.next39 +// CHECK: expand.next39: +// CHECK-NEXT: store i32 5, ptr %x40, align 4 +// CHECK-NEXT: %34 = load i32, ptr %x40, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %34) +// CHECK-NEXT: br label %expand.end41 +// CHECK: expand.end41: +// CHECK-NEXT: %a = getelementptr inbounds nuw %struct.X, ptr %x42, i32 0, i32 0 +// CHECK-NEXT: %35 = load i32, ptr %ts.addr, align 4 +// CHECK-NEXT: store i32 %35, ptr %a, align 4 +// CHECK-NEXT: %b = getelementptr inbounds nuw %struct.X, ptr %x42, i32 0, i32 1 +// CHECK-NEXT: %36 = load i32, ptr %ts.addr2, align 4 +// CHECK-NEXT: store i32 %36, ptr %b, align 4 +// CHECK-NEXT: %c = getelementptr inbounds nuw %struct.X, ptr %x42, i32 0, i32 2 +// CHECK-NEXT: %37 = load i32, ptr %ts.addr4, align 4 +// CHECK-NEXT: store i32 %37, ptr %c, align 4 +// CHECK-NEXT: %a43 = getelementptr inbounds nuw %struct.X, ptr %x42, i32 0, i32 0 +// CHECK-NEXT: %38 = load i32, ptr %a43, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %38) +// CHECK-NEXT: br label %expand.next44 +// CHECK: expand.next44: +// CHECK-NEXT: %a46 = getelementptr inbounds nuw %struct.X, ptr %x45, i32 0, i32 0 +// CHECK-NEXT: %39 = load i32, ptr %ts.addr, align 4 +// CHECK-NEXT: store i32 %39, ptr %a46, align 4 +// CHECK-NEXT: %b47 = getelementptr inbounds nuw %struct.X, ptr %x45, i32 0, i32 1 +// CHECK-NEXT: %40 = load i32, ptr %ts.addr2, align 4 +// CHECK-NEXT: store i32 %40, ptr %b47, align 4 +// CHECK-NEXT: %c48 = getelementptr inbounds nuw %struct.X, ptr %x45, i32 0, i32 2 +// CHECK-NEXT: %41 = load i32, ptr %ts.addr4, align 4 +// CHECK-NEXT: store i32 %41, ptr %c48, align 4 +// CHECK-NEXT: %a49 = getelementptr inbounds nuw %struct.X, ptr %x45, i32 0, i32 0 +// CHECK-NEXT: %42 = load i32, ptr %a49, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %42) +// CHECK-NEXT: br label %expand.next50 +// CHECK: expand.next50: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %x51, ptr align 4 @__const._Z2t3IJiiiEEvDpT_.x, i64 12, i1 false) +// CHECK-NEXT: %a52 = getelementptr inbounds nuw %struct.X, ptr %x51, i32 0, i32 0 +// CHECK-NEXT: %43 = load i32, ptr %a52, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %43) +// CHECK-NEXT: br label %expand.end53 +// CHECK: expand.end53: +// CHECK-NEXT: %a55 = getelementptr inbounds nuw %struct.X, ptr %x54, i32 0, i32 0 +// CHECK-NEXT: %44 = load i32, ptr %ts.addr, align 4 +// CHECK-NEXT: store i32 %44, ptr %a55, align 4 +// CHECK-NEXT: %b56 = getelementptr inbounds nuw %struct.X, ptr %x54, i32 0, i32 1 +// CHECK-NEXT: %45 = load i32, ptr %ts.addr2, align 4 +// CHECK-NEXT: store i32 %45, ptr %b56, align 4 +// CHECK-NEXT: %c57 = getelementptr inbounds nuw %struct.X, ptr %x54, i32 0, i32 2 +// CHECK-NEXT: %46 = load i32, ptr %ts.addr4, align 4 +// CHECK-NEXT: store i32 %46, ptr %c57, align 4 +// CHECK-NEXT: %a58 = getelementptr inbounds nuw %struct.X, ptr %x54, i32 0, i32 0 +// CHECK-NEXT: %47 = load i32, ptr %a58, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %47) +// CHECK-NEXT: br label %expand.end59 +// CHECK: expand.end59: +// CHECK-NEXT: ret void + + +// CHECK-LABEL: define {{.*}} void @_Z2t4IJLi42ELi43ELi44EEEvv() +// CHECK: entry: +// CHECK-NEXT: %x = alloca i32, align 4 +// CHECK-NEXT: %x1 = alloca i32, align 4 +// CHECK-NEXT: %x4 = alloca i32, align 4 +// CHECK-NEXT: %x6 = alloca i32, align 4 +// CHECK-NEXT: %x9 = alloca i32, align 4 +// CHECK-NEXT: %x12 = alloca i32, align 4 +// CHECK-NEXT: %x15 = alloca i32, align 4 +// CHECK-NEXT: %x18 = alloca i32, align 4 +// CHECK-NEXT: %x21 = alloca i32, align 4 +// CHECK-NEXT: %x24 = alloca i32, align 4 +// CHECK-NEXT: %x27 = alloca i32, align 4 +// CHECK-NEXT: %x30 = alloca i32, align 4 +// CHECK-NEXT: %x33 = alloca i32, align 4 +// CHECK-NEXT: %x36 = alloca i32, align 4 +// CHECK-NEXT: %x39 = alloca i32, align 4 +// CHECK-NEXT: %x42 = alloca i32, align 4 +// CHECK-NEXT: %x45 = alloca i32, align 4 +// CHECK-NEXT: %x48 = alloca i32, align 4 +// CHECK-NEXT: %x51 = alloca i32, align 4 +// CHECK-NEXT: %x54 = alloca i32, align 4 +// CHECK-NEXT: %x57 = alloca %struct.X, align 4 +// CHECK-NEXT: %x60 = alloca %struct.X, align 4 +// CHECK-NEXT: %x63 = alloca %struct.X, align 4 +// CHECK-NEXT: %x66 = alloca %struct.X, align 4 +// CHECK-NEXT: store i32 42, ptr %x, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 42) +// CHECK-NEXT: %call = call {{.*}} i32 @_Z2tgILi42EEiv() +// CHECK-NEXT: br label %expand.next +// CHECK: expand.next: +// CHECK-NEXT: store i32 43, ptr %x1, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 43) +// CHECK-NEXT: %call2 = call {{.*}} i32 @_Z2tgILi43EEiv() +// CHECK-NEXT: br label %expand.next3 +// CHECK: expand.next3: +// CHECK-NEXT: store i32 44, ptr %x4, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 44) +// CHECK-NEXT: %call5 = call {{.*}} i32 @_Z2tgILi44EEiv() +// CHECK-NEXT: br label %expand.end +// CHECK: expand.end: +// CHECK-NEXT: store i32 1, ptr %x6, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 1) +// CHECK-NEXT: %call7 = call {{.*}} i32 @_Z2tgILi1EEiv() +// CHECK-NEXT: br label %expand.next8 +// CHECK: expand.next8: +// CHECK-NEXT: store i32 42, ptr %x9, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 42) +// CHECK-NEXT: %call10 = call {{.*}} i32 @_Z2tgILi42EEiv() +// CHECK-NEXT: br label %expand.next11 +// CHECK: expand.next11: +// CHECK-NEXT: store i32 43, ptr %x12, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 43) +// CHECK-NEXT: %call13 = call {{.*}} i32 @_Z2tgILi43EEiv() +// CHECK-NEXT: br label %expand.next14 +// CHECK: expand.next14: +// CHECK-NEXT: store i32 44, ptr %x15, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 44) +// CHECK-NEXT: %call16 = call {{.*}} i32 @_Z2tgILi44EEiv() +// CHECK-NEXT: br label %expand.next17 +// CHECK: expand.next17: +// CHECK-NEXT: store i32 2, ptr %x18, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 2) +// CHECK-NEXT: %call19 = call {{.*}} i32 @_Z2tgILi2EEiv() +// CHECK-NEXT: br label %expand.next20 +// CHECK: expand.next20: +// CHECK-NEXT: store i32 42, ptr %x21, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 42) +// CHECK-NEXT: %call22 = call {{.*}} i32 @_Z2tgILi42EEiv() +// CHECK-NEXT: br label %expand.next23 +// CHECK: expand.next23: +// CHECK-NEXT: store i32 43, ptr %x24, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 43) +// CHECK-NEXT: %call25 = call {{.*}} i32 @_Z2tgILi43EEiv() +// CHECK-NEXT: br label %expand.next26 +// CHECK: expand.next26: +// CHECK-NEXT: store i32 44, ptr %x27, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 44) +// CHECK-NEXT: %call28 = call {{.*}} i32 @_Z2tgILi44EEiv() +// CHECK-NEXT: br label %expand.next29 +// CHECK: expand.next29: +// CHECK-NEXT: store i32 3, ptr %x30, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 3) +// CHECK-NEXT: %call31 = call {{.*}} i32 @_Z2tgILi3EEiv() +// CHECK-NEXT: br label %expand.end32 +// CHECK: expand.end32: +// CHECK-NEXT: store i32 4, ptr %x33, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 4) +// CHECK-NEXT: %call34 = call {{.*}} i32 @_Z2tgILi4EEiv() +// CHECK-NEXT: br label %expand.next35 +// CHECK: expand.next35: +// CHECK-NEXT: store i32 42, ptr %x36, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 42) +// CHECK-NEXT: %call37 = call {{.*}} i32 @_Z2tgILi42EEiv() +// CHECK-NEXT: br label %expand.next38 +// CHECK: expand.next38: +// CHECK-NEXT: store i32 43, ptr %x39, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 43) +// CHECK-NEXT: %call40 = call {{.*}} i32 @_Z2tgILi43EEiv() +// CHECK-NEXT: br label %expand.next41 +// CHECK: expand.next41: +// CHECK-NEXT: store i32 44, ptr %x42, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 44) +// CHECK-NEXT: %call43 = call {{.*}} i32 @_Z2tgILi44EEiv() +// CHECK-NEXT: br label %expand.next44 +// CHECK: expand.next44: +// CHECK-NEXT: store i32 42, ptr %x45, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 42) +// CHECK-NEXT: %call46 = call {{.*}} i32 @_Z2tgILi42EEiv() +// CHECK-NEXT: br label %expand.next47 +// CHECK: expand.next47: +// CHECK-NEXT: store i32 43, ptr %x48, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 43) +// CHECK-NEXT: %call49 = call {{.*}} i32 @_Z2tgILi43EEiv() +// CHECK-NEXT: br label %expand.next50 +// CHECK: expand.next50: +// CHECK-NEXT: store i32 44, ptr %x51, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 44) +// CHECK-NEXT: %call52 = call {{.*}} i32 @_Z2tgILi44EEiv() +// CHECK-NEXT: br label %expand.next53 +// CHECK: expand.next53: +// CHECK-NEXT: store i32 5, ptr %x54, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 5) +// CHECK-NEXT: %call55 = call {{.*}} i32 @_Z2tgILi5EEiv() +// CHECK-NEXT: br label %expand.end56 +// CHECK: expand.end56: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %x57, ptr align 4 @__const._Z2t4IJLi42ELi43ELi44EEEvv.x, i64 12, i1 false) +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 42) +// CHECK-NEXT: %call58 = call {{.*}} i32 @_Z2tgILi42EEiv() +// CHECK-NEXT: br label %expand.next59 +// CHECK: expand.next59: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %x60, ptr align 4 @__const._Z2t4IJLi42ELi43ELi44EEEvv.x.3, i64 12, i1 false) +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 42) +// CHECK-NEXT: %call61 = call {{.*}} i32 @_Z2tgILi42EEiv() +// CHECK-NEXT: br label %expand.next62 +// CHECK: expand.next62: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %x63, ptr align 4 @__const._Z2t4IJLi42ELi43ELi44EEEvv.x.4, i64 12, i1 false) +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 6) +// CHECK-NEXT: %call64 = call {{.*}} i32 @_Z2tgILi6EEiv() +// CHECK-NEXT: br label %expand.end65 +// CHECK: expand.end65: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %x66, ptr align 4 @__const._Z2t4IJLi42ELi43ELi44EEEvv.x.5, i64 12, i1 false) +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 42) +// CHECK-NEXT: %call67 = call {{.*}} i32 @_Z2tgILi42EEiv() +// CHECK-NEXT: br label %expand.end68 +// CHECK: expand.end68: +// CHECK-NEXT: ret void + + +// CHECK-LABEL: define {{.*}} void @_ZN2s2IJLi1ELi2ELi3EEE2tfIJLi4ELi5ELi6EEEEvv(ptr {{.*}} %this) +// CHECK: entry: +// CHECK-NEXT: %this.addr = alloca ptr, align 8 +// CHECK-NEXT: %x = alloca i32, align 4 +// CHECK-NEXT: %x2 = alloca i32, align 4 +// CHECK-NEXT: %x4 = alloca i32, align 4 +// CHECK-NEXT: %x6 = alloca i32, align 4 +// CHECK-NEXT: %x8 = alloca i32, align 4 +// CHECK-NEXT: %x10 = alloca i32, align 4 +// CHECK-NEXT: %x11 = alloca %struct.X, align 4 +// CHECK-NEXT: %x13 = alloca %struct.X, align 4 +// CHECK-NEXT: %x16 = alloca i32, align 4 +// CHECK-NEXT: %x18 = alloca i32, align 4 +// CHECK-NEXT: %x21 = alloca i32, align 4 +// CHECK-NEXT: %x24 = alloca i32, align 4 +// CHECK-NEXT: %x27 = alloca i32, align 4 +// CHECK-NEXT: %x30 = alloca i32, align 4 +// CHECK-NEXT: %x33 = alloca %struct.X, align 4 +// CHECK-NEXT: %x36 = alloca %struct.X, align 4 +// CHECK-NEXT: store ptr %this, ptr %this.addr, align 8 +// CHECK-NEXT: %this1 = load ptr, ptr %this.addr, align 8 +// CHECK-NEXT: store i32 1, ptr %x, align 4 +// CHECK-NEXT: %0 = load i32, ptr %x, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %0) +// CHECK-NEXT: br label %expand.next +// CHECK: expand.next: +// CHECK-NEXT: store i32 2, ptr %x2, align 4 +// CHECK-NEXT: %1 = load i32, ptr %x2, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %1) +// CHECK-NEXT: br label %expand.next3 +// CHECK: expand.next3: +// CHECK-NEXT: store i32 3, ptr %x4, align 4 +// CHECK-NEXT: %2 = load i32, ptr %x4, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %2) +// CHECK-NEXT: br label %expand.next5 +// CHECK: expand.next5: +// CHECK-NEXT: store i32 4, ptr %x6, align 4 +// CHECK-NEXT: %3 = load i32, ptr %x6, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %3) +// CHECK-NEXT: br label %expand.next7 +// CHECK: expand.next7: +// CHECK-NEXT: store i32 5, ptr %x8, align 4 +// CHECK-NEXT: %4 = load i32, ptr %x8, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %4) +// CHECK-NEXT: br label %expand.next9 +// CHECK: expand.next9: +// CHECK-NEXT: store i32 6, ptr %x10, align 4 +// CHECK-NEXT: %5 = load i32, ptr %x10, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %5) +// CHECK-NEXT: br label %expand.end +// CHECK: expand.end: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %x11, ptr align 4 @__const._ZN2s2IJLi1ELi2ELi3EEE2tfIJLi4ELi5ELi6EEEEvv.x, i64 12, i1 false) +// CHECK-NEXT: %a = getelementptr inbounds nuw %struct.X, ptr %x11, i32 0, i32 0 +// CHECK-NEXT: %6 = load i32, ptr %a, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %6) +// CHECK-NEXT: br label %expand.next12 +// CHECK: expand.next12: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %x13, ptr align 4 @__const._ZN2s2IJLi1ELi2ELi3EEE2tfIJLi4ELi5ELi6EEEEvv.x.6, i64 12, i1 false) +// CHECK-NEXT: %a14 = getelementptr inbounds nuw %struct.X, ptr %x13, i32 0, i32 0 +// CHECK-NEXT: %7 = load i32, ptr %a14, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %7) +// CHECK-NEXT: br label %expand.end15 +// CHECK: expand.end15: +// CHECK-NEXT: store i32 1, ptr %x16, align 4 +// CHECK-NEXT: %call = call {{.*}} i32 @_Z2tgILi1EEiv() +// CHECK-NEXT: br label %expand.next17 +// CHECK: expand.next17: +// CHECK-NEXT: store i32 2, ptr %x18, align 4 +// CHECK-NEXT: %call19 = call {{.*}} i32 @_Z2tgILi2EEiv() +// CHECK-NEXT: br label %expand.next20 +// CHECK: expand.next20: +// CHECK-NEXT: store i32 3, ptr %x21, align 4 +// CHECK-NEXT: %call22 = call {{.*}} i32 @_Z2tgILi3EEiv() +// CHECK-NEXT: br label %expand.next23 +// CHECK: expand.next23: +// CHECK-NEXT: store i32 4, ptr %x24, align 4 +// CHECK-NEXT: %call25 = call {{.*}} i32 @_Z2tgILi4EEiv() +// CHECK-NEXT: br label %expand.next26 +// CHECK: expand.next26: +// CHECK-NEXT: store i32 5, ptr %x27, align 4 +// CHECK-NEXT: %call28 = call {{.*}} i32 @_Z2tgILi5EEiv() +// CHECK-NEXT: br label %expand.next29 +// CHECK: expand.next29: +// CHECK-NEXT: store i32 6, ptr %x30, align 4 +// CHECK-NEXT: %call31 = call {{.*}} i32 @_Z2tgILi6EEiv() +// CHECK-NEXT: br label %expand.end32 +// CHECK: expand.end32: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %x33, ptr align 4 @__const._ZN2s2IJLi1ELi2ELi3EEE2tfIJLi4ELi5ELi6EEEEvv.x.7, i64 12, i1 false) +// CHECK-NEXT: %call34 = call {{.*}} i32 @_Z2tgILi1EEiv() +// CHECK-NEXT: br label %expand.next35 +// CHECK: expand.next35: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %x36, ptr align 4 @__const._ZN2s2IJLi1ELi2ELi3EEE2tfIJLi4ELi5ELi6EEEEvv.x.8, i64 12, i1 false) +// CHECK-NEXT: %call37 = call {{.*}} i32 @_Z2tgILi4EEiv() +// CHECK-NEXT: br label %expand.end38 +// CHECK: expand.end38: +// CHECK-NEXT: ret void + + +// CHECK-LABEL: define {{.*}} void @_Z2f8v() +// CHECK: entry: +// CHECK-NEXT: call void @_Z2t5IJLi1ELi2ELi3EEEvv() +// CHECK-NEXT: ret void + + +// CHECK-LABEL: define {{.*}} void @_Z2t5IJLi1ELi2ELi3EEEvv() +// CHECK: entry: +// CHECK-NEXT: %ref.tmp = alloca %class.anon, align 1 +// CHECK-NEXT: %ref.tmp1 = alloca %class.anon.1, align 1 +// CHECK-NEXT: %ref.tmp2 = alloca %class.anon.3, align 1 +// CHECK-NEXT: call void @_ZZ2t5IJLi1ELi2ELi3EEEvvENKUlvE1_clEv(ptr {{.*}} %ref.tmp) +// CHECK-NEXT: call void @_ZZ2t5IJLi1ELi2ELi3EEEvvENKUlvE0_clEv(ptr {{.*}} %ref.tmp1) +// CHECK-NEXT: call void @_ZZ2t5IJLi1ELi2ELi3EEEvvENKUlvE_clEv(ptr {{.*}} %ref.tmp2) +// CHECK-NEXT: ret void + + +// CHECK-LABEL: define {{.*}} i32 @_Z22references_enumeratingv() +// CHECK: entry: +// CHECK-NEXT: %x = alloca i32, align 4 +// CHECK-NEXT: %y = alloca i32, align 4 +// CHECK-NEXT: %z = alloca i32, align 4 +// CHECK-NEXT: %v = alloca ptr, align 8 +// CHECK-NEXT: %v1 = alloca ptr, align 8 +// CHECK-NEXT: %v4 = alloca ptr, align 8 +// CHECK-NEXT: %v6 = alloca ptr, align 8 +// CHECK-NEXT: %v9 = alloca ptr, align 8 +// CHECK-NEXT: %v12 = alloca ptr, align 8 +// CHECK-NEXT: store i32 1, ptr %x, align 4 +// CHECK-NEXT: store i32 2, ptr %y, align 4 +// CHECK-NEXT: store i32 3, ptr %z, align 4 +// CHECK-NEXT: store ptr %x, ptr %v, align 8 +// CHECK-NEXT: %0 = load ptr, ptr %v, align 8 +// CHECK-NEXT: %1 = load i32, ptr %0, align 4 +// CHECK-NEXT: %inc = add nsw i32 %1, 1 +// CHECK-NEXT: store i32 %inc, ptr %0, align 4 +// CHECK-NEXT: br label %expand.next +// CHECK: expand.next: +// CHECK-NEXT: store ptr %y, ptr %v1, align 8 +// CHECK-NEXT: %2 = load ptr, ptr %v1, align 8 +// CHECK-NEXT: %3 = load i32, ptr %2, align 4 +// CHECK-NEXT: %inc2 = add nsw i32 %3, 1 +// CHECK-NEXT: store i32 %inc2, ptr %2, align 4 +// CHECK-NEXT: br label %expand.next3 +// CHECK: expand.next3: +// CHECK-NEXT: store ptr %z, ptr %v4, align 8 +// CHECK-NEXT: %4 = load ptr, ptr %v4, align 8 +// CHECK-NEXT: %5 = load i32, ptr %4, align 4 +// CHECK-NEXT: %inc5 = add nsw i32 %5, 1 +// CHECK-NEXT: store i32 %inc5, ptr %4, align 4 +// CHECK-NEXT: br label %expand.end +// CHECK: expand.end: +// CHECK-NEXT: store ptr %x, ptr %v6, align 8 +// CHECK-NEXT: %6 = load ptr, ptr %v6, align 8 +// CHECK-NEXT: %7 = load i32, ptr %6, align 4 +// CHECK-NEXT: %inc7 = add nsw i32 %7, 1 +// CHECK-NEXT: store i32 %inc7, ptr %6, align 4 +// CHECK-NEXT: br label %expand.next8 +// CHECK: expand.next8: +// CHECK-NEXT: store ptr %y, ptr %v9, align 8 +// CHECK-NEXT: %8 = load ptr, ptr %v9, align 8 +// CHECK-NEXT: %9 = load i32, ptr %8, align 4 +// CHECK-NEXT: %inc10 = add nsw i32 %9, 1 +// CHECK-NEXT: store i32 %inc10, ptr %8, align 4 +// CHECK-NEXT: br label %expand.next11 +// CHECK: expand.next11: +// CHECK-NEXT: store ptr %z, ptr %v12, align 8 +// CHECK-NEXT: %10 = load ptr, ptr %v12, align 8 +// CHECK-NEXT: %11 = load i32, ptr %10, align 4 +// CHECK-NEXT: %inc13 = add nsw i32 %11, 1 +// CHECK-NEXT: store i32 %inc13, ptr %10, align 4 +// CHECK-NEXT: br label %expand.end14 +// CHECK: expand.end14: +// CHECK-NEXT: %12 = load i32, ptr %x, align 4 +// CHECK-NEXT: %13 = load i32, ptr %y, align 4 +// CHECK-NEXT: %add = add nsw i32 %12, %13 +// CHECK-NEXT: %14 = load i32, ptr %z, align 4 +// CHECK-NEXT: %add15 = add nsw i32 %add, %14 +// CHECK-NEXT: ret i32 %add15 + + +// CHECK-LABEL: define {{.*}} void @_ZZ2t5IJLi1ELi2ELi3EEEvvENKUlvE1_clEv(ptr {{.*}} %this) +// CHECK: entry: +// CHECK-NEXT: %this.addr = alloca ptr, align 8 +// CHECK-NEXT: %x = alloca i32, align 4 +// CHECK-NEXT: store ptr %this, ptr %this.addr, align 8 +// CHECK-NEXT: %this1 = load ptr, ptr %this.addr, align 8 +// CHECK-NEXT: store i32 1, ptr %x, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 1) +// CHECK-NEXT: %call = call {{.*}} i32 @_Z2tgILi1EEiv() +// CHECK-NEXT: br label %expand.end +// CHECK: expand.end: +// CHECK-NEXT: ret void + + +// CHECK-LABEL: define {{.*}} void @_ZZ2t5IJLi1ELi2ELi3EEEvvENKUlvE0_clEv(ptr {{.*}} %this) +// CHECK: entry: +// CHECK-NEXT: %this.addr = alloca ptr, align 8 +// CHECK-NEXT: %x = alloca i32, align 4 +// CHECK-NEXT: store ptr %this, ptr %this.addr, align 8 +// CHECK-NEXT: %this1 = load ptr, ptr %this.addr, align 8 +// CHECK-NEXT: store i32 2, ptr %x, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 2) +// CHECK-NEXT: %call = call {{.*}} i32 @_Z2tgILi2EEiv() +// CHECK-NEXT: br label %expand.end +// CHECK: expand.end: +// CHECK-NEXT: ret void + + +// CHECK-LABEL: define {{.*}} void @_ZZ2t5IJLi1ELi2ELi3EEEvvENKUlvE_clEv(ptr {{.*}} %this) +// CHECK: entry: +// CHECK-NEXT: %this.addr = alloca ptr, align 8 +// CHECK-NEXT: %x = alloca i32, align 4 +// CHECK-NEXT: store ptr %this, ptr %this.addr, align 8 +// CHECK-NEXT: %this1 = load ptr, ptr %this.addr, align 8 +// CHECK-NEXT: store i32 3, ptr %x, align 4 +// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 3) +// CHECK-NEXT: %call = call {{.*}} i32 @_Z2tgILi3EEiv() +// CHECK-NEXT: br label %expand.end +// CHECK: expand.end: +// CHECK-NEXT: ret void diff --git a/clang/test/CodeGenCXX/cxx2c-expansion-stmts-control-flow.cpp b/clang/test/CodeGenCXX/cxx2c-expansion-stmts-control-flow.cpp new file mode 100644 index 0000000000000..f94b580290500 --- /dev/null +++ b/clang/test/CodeGenCXX/cxx2c-expansion-stmts-control-flow.cpp @@ -0,0 +1,429 @@ +// RUN: %clang_cc1 -std=c++2c -triple x86_64-unknown-linux-gnu -emit-llvm -o - %s | FileCheck %s + +void h(int, int); + +void break_continue() { + template for (auto x : {1, 2}) { + break; + h(1, x); + } + + template for (auto x : {3, 4}) { + continue; + h(2, x); + } + + template for (auto x : {5, 6}) { + if (x == 2) break; + h(3, x); + } + + template for (auto x : {7, 8}) { + if (x == 2) continue; + h(4, x); + } +} + +int break_continue_nested() { + int sum = 0; + + template for (auto x : {1, 2}) { + template for (auto y : {3, 4}) { + if (x == 2) break; + sum += y; + } + sum += x; + } + + template for (auto x : {5, 6}) { + template for (auto y : {7, 8}) { + if (x == 6) continue; + sum += y; + } + sum += x; + } + + return sum; +} + +void label() { + // Only local labels are allowed in expansion statements. + template for (auto x : {1, 2, 3}) { + __label__ a; + if (x == 1) goto a; + h(1, x); + a:; + } +} + +void nested_label() { + template for (auto x : {1, 2}) { + __label__ a; + template for (auto y : {3, 4}) { + if (y == 3) goto a; + if (y == 4) goto end; + h(x, y); + } + a:; + } + end: +} + + +// CHECK-LABEL: define {{.*}} void @_Z14break_continuev() +// CHECK: entry: +// CHECK-NEXT: %x = alloca i32, align 4 +// CHECK-NEXT: %x1 = alloca i32, align 4 +// CHECK-NEXT: %x2 = alloca i32, align 4 +// CHECK-NEXT: %x4 = alloca i32, align 4 +// CHECK-NEXT: %x6 = alloca i32, align 4 +// CHECK-NEXT: %x11 = alloca i32, align 4 +// CHECK-NEXT: %x16 = alloca i32, align 4 +// CHECK-NEXT: store i32 1, ptr %x, align 4 +// CHECK-NEXT: br label %expand.end +// CHECK: expand.end: +// CHECK-NEXT: store i32 3, ptr %x1, align 4 +// CHECK-NEXT: br label %expand.next +// CHECK: expand.next: +// CHECK-NEXT: store i32 4, ptr %x2, align 4 +// CHECK-NEXT: br label %expand.end3 +// CHECK: expand.end3: +// CHECK-NEXT: store i32 5, ptr %x4, align 4 +// CHECK-NEXT: %0 = load i32, ptr %x4, align 4 +// CHECK-NEXT: %cmp = icmp eq i32 %0, 2 +// CHECK-NEXT: br i1 %cmp, label %if.then, label %if.end +// CHECK: if.then: +// CHECK-NEXT: br label %expand.end10 +// CHECK: if.end: +// CHECK-NEXT: %1 = load i32, ptr %x4, align 4 +// CHECK-NEXT: call void @_Z1hii(i32 {{.*}} 3, i32 {{.*}} %1) +// CHECK-NEXT: br label %expand.next5 +// CHECK: expand.next5: +// CHECK-NEXT: store i32 6, ptr %x6, align 4 +// CHECK-NEXT: %2 = load i32, ptr %x6, align 4 +// CHECK-NEXT: %cmp7 = icmp eq i32 %2, 2 +// CHECK-NEXT: br i1 %cmp7, label %if.then8, label %if.end9 +// CHECK: if.then8: +// CHECK-NEXT: br label %expand.end10 +// CHECK: if.end9: +// CHECK-NEXT: %3 = load i32, ptr %x6, align 4 +// CHECK-NEXT: call void @_Z1hii(i32 {{.*}} 3, i32 {{.*}} %3) +// CHECK-NEXT: br label %expand.end10 +// CHECK: expand.end10: +// CHECK-NEXT: store i32 7, ptr %x11, align 4 +// CHECK-NEXT: %4 = load i32, ptr %x11, align 4 +// CHECK-NEXT: %cmp12 = icmp eq i32 %4, 2 +// CHECK-NEXT: br i1 %cmp12, label %if.then13, label %if.end14 +// CHECK: if.then13: +// CHECK-NEXT: br label %expand.next15 +// CHECK: if.end14: +// CHECK-NEXT: %5 = load i32, ptr %x11, align 4 +// CHECK-NEXT: call void @_Z1hii(i32 {{.*}} 4, i32 {{.*}} %5) +// CHECK-NEXT: br label %expand.next15 +// CHECK: expand.next15: +// CHECK-NEXT: store i32 8, ptr %x16, align 4 +// CHECK-NEXT: %6 = load i32, ptr %x16, align 4 +// CHECK-NEXT: %cmp17 = icmp eq i32 %6, 2 +// CHECK-NEXT: br i1 %cmp17, label %if.then18, label %if.end19 +// CHECK: if.then18: +// CHECK-NEXT: br label %expand.end20 +// CHECK: if.end19: +// CHECK-NEXT: %7 = load i32, ptr %x16, align 4 +// CHECK-NEXT: call void @_Z1hii(i32 {{.*}} 4, i32 {{.*}} %7) +// CHECK-NEXT: br label %expand.end20 +// CHECK: expand.end20: +// CHECK-NEXT: ret void + + +// CHECK-LABEL: define {{.*}} i32 @_Z21break_continue_nestedv() +// CHECK: entry: +// CHECK-NEXT: %sum = alloca i32, align 4 +// CHECK-NEXT: %x = alloca i32, align 4 +// CHECK-NEXT: %y = alloca i32, align 4 +// CHECK-NEXT: %y1 = alloca i32, align 4 +// CHECK-NEXT: %x8 = alloca i32, align 4 +// CHECK-NEXT: %y9 = alloca i32, align 4 +// CHECK-NEXT: %y15 = alloca i32, align 4 +// CHECK-NEXT: %x23 = alloca i32, align 4 +// CHECK-NEXT: %y24 = alloca i32, align 4 +// CHECK-NEXT: %y30 = alloca i32, align 4 +// CHECK-NEXT: %x38 = alloca i32, align 4 +// CHECK-NEXT: %y39 = alloca i32, align 4 +// CHECK-NEXT: %y45 = alloca i32, align 4 +// CHECK-NEXT: store i32 0, ptr %sum, align 4 +// CHECK-NEXT: store i32 1, ptr %x, align 4 +// CHECK-NEXT: store i32 3, ptr %y, align 4 +// CHECK-NEXT: %0 = load i32, ptr %x, align 4 +// CHECK-NEXT: %cmp = icmp eq i32 %0, 2 +// CHECK-NEXT: br i1 %cmp, label %if.then, label %if.end +// CHECK: if.then: +// CHECK-NEXT: br label %expand.end +// CHECK: if.end: +// CHECK-NEXT: %1 = load i32, ptr %y, align 4 +// CHECK-NEXT: %2 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %add = add nsw i32 %2, %1 +// CHECK-NEXT: store i32 %add, ptr %sum, align 4 +// CHECK-NEXT: br label %expand.next +// CHECK: expand.next: +// CHECK-NEXT: store i32 4, ptr %y1, align 4 +// CHECK-NEXT: %3 = load i32, ptr %x, align 4 +// CHECK-NEXT: %cmp2 = icmp eq i32 %3, 2 +// CHECK-NEXT: br i1 %cmp2, label %if.then3, label %if.end4 +// CHECK: if.then3: +// CHECK-NEXT: br label %expand.end +// CHECK: if.end4: +// CHECK-NEXT: %4 = load i32, ptr %y1, align 4 +// CHECK-NEXT: %5 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %add5 = add nsw i32 %5, %4 +// CHECK-NEXT: store i32 %add5, ptr %sum, align 4 +// CHECK-NEXT: br label %expand.end +// CHECK: expand.end: +// CHECK-NEXT: %6 = load i32, ptr %x, align 4 +// CHECK-NEXT: %7 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %add6 = add nsw i32 %7, %6 +// CHECK-NEXT: store i32 %add6, ptr %sum, align 4 +// CHECK-NEXT: br label %expand.next7 +// CHECK: expand.next7: +// CHECK-NEXT: store i32 2, ptr %x8, align 4 +// CHECK-NEXT: store i32 3, ptr %y9, align 4 +// CHECK-NEXT: %8 = load i32, ptr %x8, align 4 +// CHECK-NEXT: %cmp10 = icmp eq i32 %8, 2 +// CHECK-NEXT: br i1 %cmp10, label %if.then11, label %if.end12 +// CHECK: if.then11: +// CHECK-NEXT: br label %expand.end20 +// CHECK: if.end12: +// CHECK-NEXT: %9 = load i32, ptr %y9, align 4 +// CHECK-NEXT: %10 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %add13 = add nsw i32 %10, %9 +// CHECK-NEXT: store i32 %add13, ptr %sum, align 4 +// CHECK-NEXT: br label %expand.next14 +// CHECK: expand.next14: +// CHECK-NEXT: store i32 4, ptr %y15, align 4 +// CHECK-NEXT: %11 = load i32, ptr %x8, align 4 +// CHECK-NEXT: %cmp16 = icmp eq i32 %11, 2 +// CHECK-NEXT: br i1 %cmp16, label %if.then17, label %if.end18 +// CHECK: if.then17: +// CHECK-NEXT: br label %expand.end20 +// CHECK: if.end18: +// CHECK-NEXT: %12 = load i32, ptr %y15, align 4 +// CHECK-NEXT: %13 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %add19 = add nsw i32 %13, %12 +// CHECK-NEXT: store i32 %add19, ptr %sum, align 4 +// CHECK-NEXT: br label %expand.end20 +// CHECK: expand.end20: +// CHECK-NEXT: %14 = load i32, ptr %x8, align 4 +// CHECK-NEXT: %15 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %add21 = add nsw i32 %15, %14 +// CHECK-NEXT: store i32 %add21, ptr %sum, align 4 +// CHECK-NEXT: br label %expand.end22 +// CHECK: expand.end22: +// CHECK-NEXT: store i32 5, ptr %x23, align 4 +// CHECK-NEXT: store i32 7, ptr %y24, align 4 +// CHECK-NEXT: %16 = load i32, ptr %x23, align 4 +// CHECK-NEXT: %cmp25 = icmp eq i32 %16, 6 +// CHECK-NEXT: br i1 %cmp25, label %if.then26, label %if.end27 +// CHECK: if.then26: +// CHECK-NEXT: br label %expand.next29 +// CHECK: if.end27: +// CHECK-NEXT: %17 = load i32, ptr %y24, align 4 +// CHECK-NEXT: %18 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %add28 = add nsw i32 %18, %17 +// CHECK-NEXT: store i32 %add28, ptr %sum, align 4 +// CHECK-NEXT: br label %expand.next29 +// CHECK: expand.next29: +// CHECK-NEXT: store i32 8, ptr %y30, align 4 +// CHECK-NEXT: %19 = load i32, ptr %x23, align 4 +// CHECK-NEXT: %cmp31 = icmp eq i32 %19, 6 +// CHECK-NEXT: br i1 %cmp31, label %if.then32, label %if.end33 +// CHECK: if.then32: +// CHECK-NEXT: br label %expand.end35 +// CHECK: if.end33: +// CHECK-NEXT: %20 = load i32, ptr %y30, align 4 +// CHECK-NEXT: %21 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %add34 = add nsw i32 %21, %20 +// CHECK-NEXT: store i32 %add34, ptr %sum, align 4 +// CHECK-NEXT: br label %expand.end35 +// CHECK: expand.end35: +// CHECK-NEXT: %22 = load i32, ptr %x23, align 4 +// CHECK-NEXT: %23 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %add36 = add nsw i32 %23, %22 +// CHECK-NEXT: store i32 %add36, ptr %sum, align 4 +// CHECK-NEXT: br label %expand.next37 +// CHECK: expand.next37: +// CHECK-NEXT: store i32 6, ptr %x38, align 4 +// CHECK-NEXT: store i32 7, ptr %y39, align 4 +// CHECK-NEXT: %24 = load i32, ptr %x38, align 4 +// CHECK-NEXT: %cmp40 = icmp eq i32 %24, 6 +// CHECK-NEXT: br i1 %cmp40, label %if.then41, label %if.end42 +// CHECK: if.then41: +// CHECK-NEXT: br label %expand.next44 +// CHECK: if.end42: +// CHECK-NEXT: %25 = load i32, ptr %y39, align 4 +// CHECK-NEXT: %26 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %add43 = add nsw i32 %26, %25 +// CHECK-NEXT: store i32 %add43, ptr %sum, align 4 +// CHECK-NEXT: br label %expand.next44 +// CHECK: expand.next44: +// CHECK-NEXT: store i32 8, ptr %y45, align 4 +// CHECK-NEXT: %27 = load i32, ptr %x38, align 4 +// CHECK-NEXT: %cmp46 = icmp eq i32 %27, 6 +// CHECK-NEXT: br i1 %cmp46, label %if.then47, label %if.end48 +// CHECK: if.then47: +// CHECK-NEXT: br label %expand.end50 +// CHECK: if.end48: +// CHECK-NEXT: %28 = load i32, ptr %y45, align 4 +// CHECK-NEXT: %29 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %add49 = add nsw i32 %29, %28 +// CHECK-NEXT: store i32 %add49, ptr %sum, align 4 +// CHECK-NEXT: br label %expand.end50 +// CHECK: expand.end50: +// CHECK-NEXT: %30 = load i32, ptr %x38, align 4 +// CHECK-NEXT: %31 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %add51 = add nsw i32 %31, %30 +// CHECK-NEXT: store i32 %add51, ptr %sum, align 4 +// CHECK-NEXT: br label %expand.end52 +// CHECK: expand.end52: +// CHECK-NEXT: %32 = load i32, ptr %sum, align 4 +// CHECK-NEXT: ret i32 %32 + + +// CHECK-LABEL: define {{.*}} void @_Z5labelv() +// CHECK: entry: +// CHECK-NEXT: %x = alloca i32, align 4 +// CHECK-NEXT: %x1 = alloca i32, align 4 +// CHECK-NEXT: %x7 = alloca i32, align 4 +// CHECK-NEXT: store i32 1, ptr %x, align 4 +// CHECK-NEXT: %0 = load i32, ptr %x, align 4 +// CHECK-NEXT: %cmp = icmp eq i32 %0, 1 +// CHECK-NEXT: br i1 %cmp, label %if.then, label %if.end +// CHECK: if.then: +// CHECK-NEXT: br label %a +// CHECK: if.end: +// CHECK-NEXT: %1 = load i32, ptr %x, align 4 +// CHECK-NEXT: call void @_Z1hii(i32 {{.*}} 1, i32 {{.*}} %1) +// CHECK-NEXT: br label %a +// CHECK: a: +// CHECK-NEXT: br label %expand.next +// CHECK: expand.next: +// CHECK-NEXT: store i32 2, ptr %x1, align 4 +// CHECK-NEXT: %2 = load i32, ptr %x1, align 4 +// CHECK-NEXT: %cmp2 = icmp eq i32 %2, 1 +// CHECK-NEXT: br i1 %cmp2, label %if.then3, label %if.end4 +// CHECK: if.then3: +// CHECK-NEXT: br label %a5 +// CHECK: if.end4: +// CHECK-NEXT: %3 = load i32, ptr %x1, align 4 +// CHECK-NEXT: call void @_Z1hii(i32 {{.*}} 1, i32 {{.*}} %3) +// CHECK-NEXT: br label %a5 +// CHECK: a5: +// CHECK-NEXT: br label %expand.next6 +// CHECK: expand.next6: +// CHECK-NEXT: store i32 3, ptr %x7, align 4 +// CHECK-NEXT: %4 = load i32, ptr %x7, align 4 +// CHECK-NEXT: %cmp8 = icmp eq i32 %4, 1 +// CHECK-NEXT: br i1 %cmp8, label %if.then9, label %if.end10 +// CHECK: if.then9: +// CHECK-NEXT: br label %a11 +// CHECK: if.end10: +// CHECK-NEXT: %5 = load i32, ptr %x7, align 4 +// CHECK-NEXT: call void @_Z1hii(i32 {{.*}} 1, i32 {{.*}} %5) +// CHECK-NEXT: br label %a11 +// CHECK: a11: +// CHECK-NEXT: br label %expand.end +// CHECK: expand.end: +// CHECK-NEXT: ret void + + +// CHECK-LABEL: define {{.*}} void @_Z12nested_labelv() +// CHECK: entry: +// CHECK-NEXT: %x = alloca i32, align 4 +// CHECK-NEXT: %y = alloca i32, align 4 +// CHECK-NEXT: %y4 = alloca i32, align 4 +// CHECK-NEXT: %x12 = alloca i32, align 4 +// CHECK-NEXT: %y13 = alloca i32, align 4 +// CHECK-NEXT: %y21 = alloca i32, align 4 +// CHECK-NEXT: store i32 1, ptr %x, align 4 +// CHECK-NEXT: store i32 3, ptr %y, align 4 +// CHECK-NEXT: %0 = load i32, ptr %y, align 4 +// CHECK-NEXT: %cmp = icmp eq i32 %0, 3 +// CHECK-NEXT: br i1 %cmp, label %if.then, label %if.end +// CHECK: if.then: +// CHECK-NEXT: br label %a +// CHECK: if.end: +// CHECK-NEXT: %1 = load i32, ptr %y, align 4 +// CHECK-NEXT: %cmp1 = icmp eq i32 %1, 4 +// CHECK-NEXT: br i1 %cmp1, label %if.then2, label %if.end3 +// CHECK: if.then2: +// CHECK-NEXT: br label %end +// CHECK: if.end3: +// CHECK-NEXT: %2 = load i32, ptr %x, align 4 +// CHECK-NEXT: %3 = load i32, ptr %y, align 4 +// CHECK-NEXT: call void @_Z1hii(i32 {{.*}} %2, i32 {{.*}} %3) +// CHECK-NEXT: br label %expand.next +// CHECK: expand.next: +// CHECK-NEXT: store i32 4, ptr %y4, align 4 +// CHECK-NEXT: %4 = load i32, ptr %y4, align 4 +// CHECK-NEXT: %cmp5 = icmp eq i32 %4, 3 +// CHECK-NEXT: br i1 %cmp5, label %if.then6, label %if.end7 +// CHECK: if.then6: +// CHECK-NEXT: br label %a +// CHECK: if.end7: +// CHECK-NEXT: %5 = load i32, ptr %y4, align 4 +// CHECK-NEXT: %cmp8 = icmp eq i32 %5, 4 +// CHECK-NEXT: br i1 %cmp8, label %if.then9, label %if.end10 +// CHECK: if.then9: +// CHECK-NEXT: br label %end +// CHECK: if.end10: +// CHECK-NEXT: %6 = load i32, ptr %x, align 4 +// CHECK-NEXT: %7 = load i32, ptr %y4, align 4 +// CHECK-NEXT: call void @_Z1hii(i32 {{.*}} %6, i32 {{.*}} %7) +// CHECK-NEXT: br label %expand.end +// CHECK: expand.end: +// CHECK-NEXT: br label %a +// CHECK: a: +// CHECK-NEXT: br label %expand.next11 +// CHECK: expand.next11: +// CHECK-NEXT: store i32 2, ptr %x12, align 4 +// CHECK-NEXT: store i32 3, ptr %y13, align 4 +// CHECK-NEXT: %8 = load i32, ptr %y13, align 4 +// CHECK-NEXT: %cmp14 = icmp eq i32 %8, 3 +// CHECK-NEXT: br i1 %cmp14, label %if.then15, label %if.end16 +// CHECK: if.then15: +// CHECK-NEXT: br label %a29 +// CHECK: if.end16: +// CHECK-NEXT: %9 = load i32, ptr %y13, align 4 +// CHECK-NEXT: %cmp17 = icmp eq i32 %9, 4 +// CHECK-NEXT: br i1 %cmp17, label %if.then18, label %if.end19 +// CHECK: if.then18: +// CHECK-NEXT: br label %end +// CHECK: if.end19: +// CHECK-NEXT: %10 = load i32, ptr %x12, align 4 +// CHECK-NEXT: %11 = load i32, ptr %y13, align 4 +// CHECK-NEXT: call void @_Z1hii(i32 {{.*}} %10, i32 {{.*}} %11) +// CHECK-NEXT: br label %expand.next20 +// CHECK: expand.next20: +// CHECK-NEXT: store i32 4, ptr %y21, align 4 +// CHECK-NEXT: %12 = load i32, ptr %y21, align 4 +// CHECK-NEXT: %cmp22 = icmp eq i32 %12, 3 +// CHECK-NEXT: br i1 %cmp22, label %if.then23, label %if.end24 +// CHECK: if.then23: +// CHECK-NEXT: br label %a29 +// CHECK: if.end24: +// CHECK-NEXT: %13 = load i32, ptr %y21, align 4 +// CHECK-NEXT: %cmp25 = icmp eq i32 %13, 4 +// CHECK-NEXT: br i1 %cmp25, label %if.then26, label %if.end27 +// CHECK: if.then26: +// CHECK-NEXT: br label %end +// CHECK: if.end27: +// CHECK-NEXT: %14 = load i32, ptr %x12, align 4 +// CHECK-NEXT: %15 = load i32, ptr %y21, align 4 +// CHECK-NEXT: call void @_Z1hii(i32 {{.*}} %14, i32 {{.*}} %15) +// CHECK-NEXT: br label %expand.end28 +// CHECK: expand.end28: +// CHECK-NEXT: br label %a29 +// CHECK: a29: +// CHECK-NEXT: br label %expand.end30 +// CHECK: expand.end30: +// CHECK-NEXT: br label %end +// CHECK: end: +// CHECK-NEXT: ret void diff --git a/clang/test/CodeGenCXX/cxx2c-expansion-stmts-templates.cpp b/clang/test/CodeGenCXX/cxx2c-expansion-stmts-templates.cpp new file mode 100644 index 0000000000000..e0de7ced5baee --- /dev/null +++ b/clang/test/CodeGenCXX/cxx2c-expansion-stmts-templates.cpp @@ -0,0 +1,208 @@ +// RUN: %clang_cc1 -std=c++2c -triple x86_64-unknown-linux-gnu -emit-llvm -o - %s | FileCheck %s + +struct E { + int x, y; + constexpr E(int x, int y) : x{x}, y{y} {} +}; + +template +int unexpanded_pack_good(Es ...es) { + int sum = 0; + ([&] { + template for (auto x : es) sum += x; + template for (Es e : {{5, 6}, {7, 8}}) sum += e.x + e.y; + }(), ...); + return sum; +} + +int unexpanded_pack() { + return unexpanded_pack_good(E{1, 2}, E{3, 4}); +} + + +// CHECK: %struct.E = type { i32, i32 } +// CHECK: %class.anon = type { ptr, ptr } +// CHECK: %class.anon.0 = type { ptr, ptr } + + +// CHECK-LABEL: define {{.*}} i32 @_Z15unexpanded_packv() +// CHECK: entry: +// CHECK-NEXT: %agg.tmp = alloca %struct.E, align 4 +// CHECK-NEXT: %agg.tmp1 = alloca %struct.E, align 4 +// CHECK-NEXT: call void @_ZN1EC1Eii(ptr {{.*}} %agg.tmp, i32 {{.*}} 1, i32 {{.*}} 2) +// CHECK-NEXT: call void @_ZN1EC1Eii(ptr {{.*}} %agg.tmp1, i32 {{.*}} 3, i32 {{.*}} 4) +// CHECK-NEXT: %0 = load i64, ptr %agg.tmp, align 4 +// CHECK-NEXT: %1 = load i64, ptr %agg.tmp1, align 4 +// CHECK-NEXT: %call = call {{.*}} i32 @_Z20unexpanded_pack_goodIJ1ES0_EEiDpT_(i64 %0, i64 %1) +// CHECK-NEXT: ret i32 %call + + +// CHECK-LABEL: define {{.*}} i32 @_Z20unexpanded_pack_goodIJ1ES0_EEiDpT_(i64 %es.coerce, i64 %es.coerce2) +// CHECK: entry: +// CHECK-NEXT: %es = alloca %struct.E, align 4 +// CHECK-NEXT: %es3 = alloca %struct.E, align 4 +// CHECK-NEXT: %sum = alloca i32, align 4 +// CHECK-NEXT: %ref.tmp = alloca %class.anon, align 8 +// CHECK-NEXT: %ref.tmp4 = alloca %class.anon.0, align 8 +// CHECK-NEXT: store i64 %es.coerce, ptr %es, align 4 +// CHECK-NEXT: store i64 %es.coerce2, ptr %es3, align 4 +// CHECK-NEXT: store i32 0, ptr %sum, align 4 +// CHECK-NEXT: %0 = getelementptr inbounds nuw %class.anon, ptr %ref.tmp, i32 0, i32 0 +// CHECK-NEXT: store ptr %es, ptr %0, align 8 +// CHECK-NEXT: %1 = getelementptr inbounds nuw %class.anon, ptr %ref.tmp, i32 0, i32 1 +// CHECK-NEXT: store ptr %sum, ptr %1, align 8 +// CHECK-NEXT: call void @_ZZ20unexpanded_pack_goodIJ1ES0_EEiDpT_ENKUlvE0_clEv(ptr {{.*}} %ref.tmp) +// CHECK-NEXT: %2 = getelementptr inbounds nuw %class.anon.0, ptr %ref.tmp4, i32 0, i32 0 +// CHECK-NEXT: store ptr %es3, ptr %2, align 8 +// CHECK-NEXT: %3 = getelementptr inbounds nuw %class.anon.0, ptr %ref.tmp4, i32 0, i32 1 +// CHECK-NEXT: store ptr %sum, ptr %3, align 8 +// CHECK-NEXT: call void @_ZZ20unexpanded_pack_goodIJ1ES0_EEiDpT_ENKUlvE_clEv(ptr {{.*}} %ref.tmp4) +// CHECK-NEXT: %4 = load i32, ptr %sum, align 4 +// CHECK-NEXT: ret i32 %4 + + +// CHECK-LABEL: define {{.*}} void @_ZN1EC1Eii(ptr {{.*}} %this, i32 {{.*}} %x, i32 {{.*}} %y) {{.*}} +// CHECK: entry: +// CHECK-NEXT: %this.addr = alloca ptr, align 8 +// CHECK-NEXT: %x.addr = alloca i32, align 4 +// CHECK-NEXT: %y.addr = alloca i32, align 4 +// CHECK-NEXT: store ptr %this, ptr %this.addr, align 8 +// CHECK-NEXT: store i32 %x, ptr %x.addr, align 4 +// CHECK-NEXT: store i32 %y, ptr %y.addr, align 4 +// CHECK-NEXT: %this1 = load ptr, ptr %this.addr, align 8 +// CHECK-NEXT: %0 = load i32, ptr %x.addr, align 4 +// CHECK-NEXT: %1 = load i32, ptr %y.addr, align 4 +// CHECK-NEXT: call void @_ZN1EC2Eii(ptr {{.*}} %this1, i32 {{.*}} %0, i32 {{.*}} %1) +// CHECK-NEXT: ret void + + +// CHECK-LABEL: define {{.*}} void @_ZZ20unexpanded_pack_goodIJ1ES0_EEiDpT_ENKUlvE0_clEv(ptr {{.*}} %this) +// CHECK: entry: +// CHECK-NEXT: %this.addr = alloca ptr, align 8 +// CHECK-NEXT: %0 = alloca ptr, align 8 +// CHECK-NEXT: %x = alloca i32, align 4 +// CHECK-NEXT: %x3 = alloca i32, align 4 +// CHECK-NEXT: %e = alloca %struct.E, align 4 +// CHECK-NEXT: %e10 = alloca %struct.E, align 4 +// CHECK-NEXT: store ptr %this, ptr %this.addr, align 8 +// CHECK-NEXT: %this1 = load ptr, ptr %this.addr, align 8 +// CHECK-NEXT: %1 = getelementptr inbounds nuw %class.anon, ptr %this1, i32 0, i32 0 +// CHECK-NEXT: %2 = load ptr, ptr %1, align 8 +// CHECK-NEXT: store ptr %2, ptr %0, align 8 +// CHECK-NEXT: %3 = load ptr, ptr %0, align 8 +// CHECK-NEXT: %x2 = getelementptr inbounds nuw %struct.E, ptr %3, i32 0, i32 0 +// CHECK-NEXT: %4 = load i32, ptr %x2, align 4 +// CHECK-NEXT: store i32 %4, ptr %x, align 4 +// CHECK-NEXT: %5 = load i32, ptr %x, align 4 +// CHECK-NEXT: %6 = getelementptr inbounds nuw %class.anon, ptr %this1, i32 0, i32 1 +// CHECK-NEXT: %7 = load ptr, ptr %6, align 8 +// CHECK-NEXT: %8 = load i32, ptr %7, align 4 +// CHECK-NEXT: %add = add nsw i32 %8, %5 +// CHECK-NEXT: store i32 %add, ptr %7, align 4 +// CHECK-NEXT: br label %expand.next +// CHECK: expand.next: +// CHECK-NEXT: %9 = load ptr, ptr %0, align 8 +// CHECK-NEXT: %y = getelementptr inbounds nuw %struct.E, ptr %9, i32 0, i32 1 +// CHECK-NEXT: %10 = load i32, ptr %y, align 4 +// CHECK-NEXT: store i32 %10, ptr %x3, align 4 +// CHECK-NEXT: %11 = load i32, ptr %x3, align 4 +// CHECK-NEXT: %12 = getelementptr inbounds nuw %class.anon, ptr %this1, i32 0, i32 1 +// CHECK-NEXT: %13 = load ptr, ptr %12, align 8 +// CHECK-NEXT: %14 = load i32, ptr %13, align 4 +// CHECK-NEXT: %add4 = add nsw i32 %14, %11 +// CHECK-NEXT: store i32 %add4, ptr %13, align 4 +// CHECK-NEXT: br label %expand.end +// CHECK: expand.end: +// CHECK-NEXT: call void @_ZN1EC1Eii(ptr {{.*}} %e, i32 {{.*}} 5, i32 {{.*}} 6) +// CHECK-NEXT: %x5 = getelementptr inbounds nuw %struct.E, ptr %e, i32 0, i32 0 +// CHECK-NEXT: %15 = load i32, ptr %x5, align 4 +// CHECK-NEXT: %y6 = getelementptr inbounds nuw %struct.E, ptr %e, i32 0, i32 1 +// CHECK-NEXT: %16 = load i32, ptr %y6, align 4 +// CHECK-NEXT: %add7 = add nsw i32 %15, %16 +// CHECK-NEXT: %17 = getelementptr inbounds nuw %class.anon, ptr %this1, i32 0, i32 1 +// CHECK-NEXT: %18 = load ptr, ptr %17, align 8 +// CHECK-NEXT: %19 = load i32, ptr %18, align 4 +// CHECK-NEXT: %add8 = add nsw i32 %19, %add7 +// CHECK-NEXT: store i32 %add8, ptr %18, align 4 +// CHECK-NEXT: br label %expand.next9 +// CHECK: expand.next9: +// CHECK-NEXT: call void @_ZN1EC1Eii(ptr {{.*}} %e10, i32 {{.*}} 7, i32 {{.*}} 8) +// CHECK-NEXT: %x11 = getelementptr inbounds nuw %struct.E, ptr %e10, i32 0, i32 0 +// CHECK-NEXT: %20 = load i32, ptr %x11, align 4 +// CHECK-NEXT: %y12 = getelementptr inbounds nuw %struct.E, ptr %e10, i32 0, i32 1 +// CHECK-NEXT: %21 = load i32, ptr %y12, align 4 +// CHECK-NEXT: %add13 = add nsw i32 %20, %21 +// CHECK-NEXT: %22 = getelementptr inbounds nuw %class.anon, ptr %this1, i32 0, i32 1 +// CHECK-NEXT: %23 = load ptr, ptr %22, align 8 +// CHECK-NEXT: %24 = load i32, ptr %23, align 4 +// CHECK-NEXT: %add14 = add nsw i32 %24, %add13 +// CHECK-NEXT: store i32 %add14, ptr %23, align 4 +// CHECK-NEXT: br label %expand.end15 +// CHECK: expand.end15: +// CHECK-NEXT: ret void + + +// CHECK-LABEL: define {{.*}} void @_ZZ20unexpanded_pack_goodIJ1ES0_EEiDpT_ENKUlvE_clEv(ptr {{.*}} %this) +// CHECK: entry: +// CHECK-NEXT: %this.addr = alloca ptr, align 8 +// CHECK-NEXT: %0 = alloca ptr, align 8 +// CHECK-NEXT: %x = alloca i32, align 4 +// CHECK-NEXT: %x3 = alloca i32, align 4 +// CHECK-NEXT: %e = alloca %struct.E, align 4 +// CHECK-NEXT: %e10 = alloca %struct.E, align 4 +// CHECK-NEXT: store ptr %this, ptr %this.addr, align 8 +// CHECK-NEXT: %this1 = load ptr, ptr %this.addr, align 8 +// CHECK-NEXT: %1 = getelementptr inbounds nuw %class.anon.0, ptr %this1, i32 0, i32 0 +// CHECK-NEXT: %2 = load ptr, ptr %1, align 8 +// CHECK-NEXT: store ptr %2, ptr %0, align 8 +// CHECK-NEXT: %3 = load ptr, ptr %0, align 8 +// CHECK-NEXT: %x2 = getelementptr inbounds nuw %struct.E, ptr %3, i32 0, i32 0 +// CHECK-NEXT: %4 = load i32, ptr %x2, align 4 +// CHECK-NEXT: store i32 %4, ptr %x, align 4 +// CHECK-NEXT: %5 = load i32, ptr %x, align 4 +// CHECK-NEXT: %6 = getelementptr inbounds nuw %class.anon.0, ptr %this1, i32 0, i32 1 +// CHECK-NEXT: %7 = load ptr, ptr %6, align 8 +// CHECK-NEXT: %8 = load i32, ptr %7, align 4 +// CHECK-NEXT: %add = add nsw i32 %8, %5 +// CHECK-NEXT: store i32 %add, ptr %7, align 4 +// CHECK-NEXT: br label %expand.next +// CHECK: expand.next: +// CHECK-NEXT: %9 = load ptr, ptr %0, align 8 +// CHECK-NEXT: %y = getelementptr inbounds nuw %struct.E, ptr %9, i32 0, i32 1 +// CHECK-NEXT: %10 = load i32, ptr %y, align 4 +// CHECK-NEXT: store i32 %10, ptr %x3, align 4 +// CHECK-NEXT: %11 = load i32, ptr %x3, align 4 +// CHECK-NEXT: %12 = getelementptr inbounds nuw %class.anon.0, ptr %this1, i32 0, i32 1 +// CHECK-NEXT: %13 = load ptr, ptr %12, align 8 +// CHECK-NEXT: %14 = load i32, ptr %13, align 4 +// CHECK-NEXT: %add4 = add nsw i32 %14, %11 +// CHECK-NEXT: store i32 %add4, ptr %13, align 4 +// CHECK-NEXT: br label %expand.end +// CHECK: expand.end: +// CHECK-NEXT: call void @_ZN1EC1Eii(ptr {{.*}} %e, i32 {{.*}} 5, i32 {{.*}} 6) +// CHECK-NEXT: %x5 = getelementptr inbounds nuw %struct.E, ptr %e, i32 0, i32 0 +// CHECK-NEXT: %15 = load i32, ptr %x5, align 4 +// CHECK-NEXT: %y6 = getelementptr inbounds nuw %struct.E, ptr %e, i32 0, i32 1 +// CHECK-NEXT: %16 = load i32, ptr %y6, align 4 +// CHECK-NEXT: %add7 = add nsw i32 %15, %16 +// CHECK-NEXT: %17 = getelementptr inbounds nuw %class.anon.0, ptr %this1, i32 0, i32 1 +// CHECK-NEXT: %18 = load ptr, ptr %17, align 8 +// CHECK-NEXT: %19 = load i32, ptr %18, align 4 +// CHECK-NEXT: %add8 = add nsw i32 %19, %add7 +// CHECK-NEXT: store i32 %add8, ptr %18, align 4 +// CHECK-NEXT: br label %expand.next9 +// CHECK: expand.next9: +// CHECK-NEXT: call void @_ZN1EC1Eii(ptr {{.*}} %e10, i32 {{.*}} 7, i32 {{.*}} 8) +// CHECK-NEXT: %x11 = getelementptr inbounds nuw %struct.E, ptr %e10, i32 0, i32 0 +// CHECK-NEXT: %20 = load i32, ptr %x11, align 4 +// CHECK-NEXT: %y12 = getelementptr inbounds nuw %struct.E, ptr %e10, i32 0, i32 1 +// CHECK-NEXT: %21 = load i32, ptr %y12, align 4 +// CHECK-NEXT: %add13 = add nsw i32 %20, %21 +// CHECK-NEXT: %22 = getelementptr inbounds nuw %class.anon.0, ptr %this1, i32 0, i32 1 +// CHECK-NEXT: %23 = load ptr, ptr %22, align 8 +// CHECK-NEXT: %24 = load i32, ptr %23, align 4 +// CHECK-NEXT: %add14 = add nsw i32 %24, %add13 +// CHECK-NEXT: store i32 %add14, ptr %23, align 4 +// CHECK-NEXT: br label %expand.end15 +// CHECK: expand.end15: +// CHECK-NEXT: ret void diff --git a/clang/test/CodeGenCXX/cxx2c-iterating-expansion-stmt.cpp b/clang/test/CodeGenCXX/cxx2c-iterating-expansion-stmt.cpp new file mode 100644 index 0000000000000..ad73972740749 --- /dev/null +++ b/clang/test/CodeGenCXX/cxx2c-iterating-expansion-stmt.cpp @@ -0,0 +1,474 @@ +// RUN: %clang_cc1 -std=c++2c -triple x86_64-unknown-linux-gnu -emit-llvm -o - %s | FileCheck %s + +template +struct Array { + T data[size]{}; + constexpr const T* begin() const { return data; } + constexpr const T* end() const { return data + size; } +}; + +int f1() { + static constexpr Array integers{1, 2, 3}; + int sum = 0; + template for (auto x : integers) sum += x; + return sum; +} + +int f2() { + static constexpr Array integers{1, 2, 3}; + int sum = 0; + template for (constexpr auto x : integers) sum += x; + return sum; +} + +int f3() { + static constexpr Array integers{}; + int sum = 0; + template for (constexpr auto x : integers) { + static_assert(false, "not expanded"); + sum += x; + } + return sum; +} + +int f4() { + static constexpr Array a{1, 2}; + static constexpr Array b{3, 4}; + int sum = 0; + + template for (auto x : a) + template for (auto y : b) + sum += x + y; + + template for (constexpr auto x : a) + template for (constexpr auto y : b) + sum += x + y; + + return sum; +} + +struct Private { + static constexpr Array integers{1, 2, 3}; + friend constexpr int friend_func(); + +private: + constexpr const int* begin() const { return integers.begin(); } + constexpr const int* end() const { return integers.end(); } + +public: + static int member_func(); +}; + +int Private::member_func() { + int sum = 0; + static constexpr Private p1; + template for (auto x : p1) sum += x; + return sum; +} + +struct CustomIterator { + struct iterator { + int n; + + constexpr iterator operator+(int m) const { + return {n + m}; + } + + constexpr int operator*() const { + return n; + } + + // FIXME: Should be '!=' once we support that properly. + friend constexpr __PTRDIFF_TYPE__ operator-(iterator a, iterator b) { + return a.n - b.n; + } + }; + + constexpr iterator begin() const { return iterator(1); } + constexpr iterator end() const { return iterator(5); } +}; + +int custom_iterator() { + static constexpr CustomIterator c; + int sum = 0; + template for (auto x : c) sum += x; + template for (constexpr auto x : c) sum += x; + return sum; +} + +// CHECK: @_ZZ2f1vE8integers = internal constant %struct.Array { [3 x i32] [i32 1, i32 2, i32 3] }, align 4 +// CHECK: @_ZZ2f2vE8integers = internal constant %struct.Array { [3 x i32] [i32 1, i32 2, i32 3] }, align 4 +// CHECK: @_ZZ2f3vE8integers = internal constant %struct.Array.0 zeroinitializer, align 4 +// CHECK: @_ZZ2f4vE1a = internal constant %struct.Array.1 { [2 x i32] [i32 1, i32 2] }, align 4 +// CHECK: @_ZZ2f4vE1b = internal constant %struct.Array.1 { [2 x i32] [i32 3, i32 4] }, align 4 +// CHECK: @_ZZN7Private11member_funcEvE2p1 = internal constant %struct.Private zeroinitializer, align 1 +// CHECK: @_ZN7Private8integersE = {{.*}} constant %struct.Array { [3 x i32] [i32 1, i32 2, i32 3] }, comdat, align 4 +// CHECK: @_ZZ15custom_iteratorvE1c = internal constant %struct.CustomIterator zeroinitializer, align 1 +// CHECK: @__const._Z15custom_iteratorv.__begin1 = private {{.*}} constant %"struct.CustomIterator::iterator" { i32 1 }, align 4 +// CHECK: @__const._Z15custom_iteratorv.__end1 = private {{.*}} constant %"struct.CustomIterator::iterator" { i32 5 }, align 4 +// CHECK: @__const._Z15custom_iteratorv.__begin1.1 = private {{.*}} constant %"struct.CustomIterator::iterator" { i32 1 }, align 4 +// CHECK: @__const._Z15custom_iteratorv.__end1.2 = private {{.*}} constant %"struct.CustomIterator::iterator" { i32 5 }, align 4 + +// CHECK-LABEL: define {{.*}} i32 @_Z2f1v() +// CHECK: entry: +// CHECK-NEXT: %sum = alloca i32, align 4 +// CHECK-NEXT: %__range1 = alloca ptr, align 8 +// CHECK-NEXT: %__begin1 = alloca ptr, align 8 +// CHECK-NEXT: %__end1 = alloca ptr, align 8 +// CHECK-NEXT: %x = alloca i32, align 4 +// CHECK-NEXT: %x1 = alloca i32, align 4 +// CHECK-NEXT: %x4 = alloca i32, align 4 +// CHECK-NEXT: store i32 0, ptr %sum, align 4 +// CHECK-NEXT: store ptr @_ZZ2f1vE8integers, ptr %__range1, align 8 +// CHECK-NEXT: store ptr @_ZZ2f1vE8integers, ptr %__begin1, align 8 +// CHECK-NEXT: store ptr getelementptr (i8, ptr @_ZZ2f1vE8integers, i64 12), ptr %__end1, align 8 +// CHECK-NEXT: %0 = load i32, ptr @_ZZ2f1vE8integers, align 4 +// CHECK-NEXT: store i32 %0, ptr %x, align 4 +// CHECK-NEXT: %1 = load i32, ptr %x, align 4 +// CHECK-NEXT: %2 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %add = add nsw i32 %2, %1 +// CHECK-NEXT: store i32 %add, ptr %sum, align 4 +// CHECK-NEXT: br label %expand.next +// CHECK: expand.next: +// CHECK-NEXT: %3 = load i32, ptr getelementptr inbounds (i32, ptr @_ZZ2f1vE8integers, i64 1), align 4 +// CHECK-NEXT: store i32 %3, ptr %x1, align 4 +// CHECK-NEXT: %4 = load i32, ptr %x1, align 4 +// CHECK-NEXT: %5 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %add2 = add nsw i32 %5, %4 +// CHECK-NEXT: store i32 %add2, ptr %sum, align 4 +// CHECK-NEXT: br label %expand.next3 +// CHECK: expand.next3: +// CHECK-NEXT: %6 = load i32, ptr getelementptr inbounds (i32, ptr @_ZZ2f1vE8integers, i64 2), align 4 +// CHECK-NEXT: store i32 %6, ptr %x4, align 4 +// CHECK-NEXT: %7 = load i32, ptr %x4, align 4 +// CHECK-NEXT: %8 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %add5 = add nsw i32 %8, %7 +// CHECK-NEXT: store i32 %add5, ptr %sum, align 4 +// CHECK-NEXT: br label %expand.end +// CHECK: expand.end: +// CHECK-NEXT: %9 = load i32, ptr %sum, align 4 +// CHECK-NEXT: ret i32 %9 + + +// CHECK-LABEL: define {{.*}} i32 @_Z2f2v() +// CHECK: entry: +// CHECK-NEXT: %sum = alloca i32, align 4 +// CHECK-NEXT: %__range1 = alloca ptr, align 8 +// CHECK-NEXT: %__begin1 = alloca ptr, align 8 +// CHECK-NEXT: %__end1 = alloca ptr, align 8 +// CHECK-NEXT: %x = alloca i32, align 4 +// CHECK-NEXT: %x1 = alloca i32, align 4 +// CHECK-NEXT: %x4 = alloca i32, align 4 +// CHECK-NEXT: store i32 0, ptr %sum, align 4 +// CHECK-NEXT: store ptr @_ZZ2f2vE8integers, ptr %__range1, align 8 +// CHECK-NEXT: store ptr @_ZZ2f2vE8integers, ptr %__begin1, align 8 +// CHECK-NEXT: store ptr getelementptr (i8, ptr @_ZZ2f2vE8integers, i64 12), ptr %__end1, align 8 +// CHECK-NEXT: store i32 1, ptr %x, align 4 +// CHECK-NEXT: %0 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %add = add nsw i32 %0, 1 +// CHECK-NEXT: store i32 %add, ptr %sum, align 4 +// CHECK-NEXT: br label %expand.next +// CHECK: expand.next: +// CHECK-NEXT: store i32 2, ptr %x1, align 4 +// CHECK-NEXT: %1 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %add2 = add nsw i32 %1, 2 +// CHECK-NEXT: store i32 %add2, ptr %sum, align 4 +// CHECK-NEXT: br label %expand.next3 +// CHECK: expand.next3: +// CHECK-NEXT: store i32 3, ptr %x4, align 4 +// CHECK-NEXT: %2 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %add5 = add nsw i32 %2, 3 +// CHECK-NEXT: store i32 %add5, ptr %sum, align 4 +// CHECK-NEXT: br label %expand.end +// CHECK: expand.end: +// CHECK-NEXT: %3 = load i32, ptr %sum, align 4 +// CHECK-NEXT: ret i32 %3 + + +// CHECK-LABEL: define {{.*}} i32 @_Z2f3v() +// CHECK: entry: +// CHECK-NEXT: %sum = alloca i32, align 4 +// CHECK-NEXT: %__range1 = alloca ptr, align 8 +// CHECK-NEXT: %__begin1 = alloca ptr, align 8 +// CHECK-NEXT: %__end1 = alloca ptr, align 8 +// CHECK-NEXT: store i32 0, ptr %sum, align 4 +// CHECK-NEXT: store ptr @_ZZ2f3vE8integers, ptr %__range1, align 8 +// CHECK-NEXT: store ptr @_ZZ2f3vE8integers, ptr %__begin1, align 8 +// CHECK-NEXT: store ptr @_ZZ2f3vE8integers, ptr %__end1, align 8 +// CHECK-NEXT: %0 = load i32, ptr %sum, align 4 +// CHECK-NEXT: ret i32 %0 + + +// CHECK-LABEL: define {{.*}} i32 @_Z2f4v() +// CHECK: entry: +// CHECK-NEXT: %sum = alloca i32, align 4 +// CHECK-NEXT: %__range1 = alloca ptr, align 8 +// CHECK-NEXT: %__begin1 = alloca ptr, align 8 +// CHECK-NEXT: %__end1 = alloca ptr, align 8 +// CHECK-NEXT: %x = alloca i32, align 4 +// CHECK-NEXT: %__range2 = alloca ptr, align 8 +// CHECK-NEXT: %__begin2 = alloca ptr, align 8 +// CHECK-NEXT: %__end2 = alloca ptr, align 8 +// CHECK-NEXT: %y = alloca i32, align 4 +// CHECK-NEXT: %y2 = alloca i32, align 4 +// CHECK-NEXT: %x6 = alloca i32, align 4 +// CHECK-NEXT: %__range27 = alloca ptr, align 8 +// CHECK-NEXT: %__begin28 = alloca ptr, align 8 +// CHECK-NEXT: %__end29 = alloca ptr, align 8 +// CHECK-NEXT: %y10 = alloca i32, align 4 +// CHECK-NEXT: %y14 = alloca i32, align 4 +// CHECK-NEXT: %__range119 = alloca ptr, align 8 +// CHECK-NEXT: %__begin120 = alloca ptr, align 8 +// CHECK-NEXT: %__end121 = alloca ptr, align 8 +// CHECK-NEXT: %x22 = alloca i32, align 4 +// CHECK-NEXT: %__range223 = alloca ptr, align 8 +// CHECK-NEXT: %__begin224 = alloca ptr, align 8 +// CHECK-NEXT: %__end225 = alloca ptr, align 8 +// CHECK-NEXT: %y26 = alloca i32, align 4 +// CHECK-NEXT: %y29 = alloca i32, align 4 +// CHECK-NEXT: %x33 = alloca i32, align 4 +// CHECK-NEXT: %__range234 = alloca ptr, align 8 +// CHECK-NEXT: %__begin235 = alloca ptr, align 8 +// CHECK-NEXT: %__end236 = alloca ptr, align 8 +// CHECK-NEXT: %y37 = alloca i32, align 4 +// CHECK-NEXT: %y40 = alloca i32, align 4 +// CHECK-NEXT: store i32 0, ptr %sum, align 4 +// CHECK-NEXT: store ptr @_ZZ2f4vE1a, ptr %__range1, align 8 +// CHECK-NEXT: store ptr @_ZZ2f4vE1a, ptr %__begin1, align 8 +// CHECK-NEXT: store ptr getelementptr (i8, ptr @_ZZ2f4vE1a, i64 8), ptr %__end1, align 8 +// CHECK-NEXT: %0 = load i32, ptr @_ZZ2f4vE1a, align 4 +// CHECK-NEXT: store i32 %0, ptr %x, align 4 +// CHECK-NEXT: store ptr @_ZZ2f4vE1b, ptr %__range2, align 8 +// CHECK-NEXT: store ptr @_ZZ2f4vE1b, ptr %__begin2, align 8 +// CHECK-NEXT: store ptr getelementptr (i8, ptr @_ZZ2f4vE1b, i64 8), ptr %__end2, align 8 +// CHECK-NEXT: %1 = load i32, ptr @_ZZ2f4vE1b, align 4 +// CHECK-NEXT: store i32 %1, ptr %y, align 4 +// CHECK-NEXT: %2 = load i32, ptr %x, align 4 +// CHECK-NEXT: %3 = load i32, ptr %y, align 4 +// CHECK-NEXT: %add = add nsw i32 %2, %3 +// CHECK-NEXT: %4 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %add1 = add nsw i32 %4, %add +// CHECK-NEXT: store i32 %add1, ptr %sum, align 4 +// CHECK-NEXT: br label %expand.next +// CHECK: expand.next: +// CHECK-NEXT: %5 = load i32, ptr getelementptr inbounds (i32, ptr @_ZZ2f4vE1b, i64 1), align 4 +// CHECK-NEXT: store i32 %5, ptr %y2, align 4 +// CHECK-NEXT: %6 = load i32, ptr %x, align 4 +// CHECK-NEXT: %7 = load i32, ptr %y2, align 4 +// CHECK-NEXT: %add3 = add nsw i32 %6, %7 +// CHECK-NEXT: %8 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %add4 = add nsw i32 %8, %add3 +// CHECK-NEXT: store i32 %add4, ptr %sum, align 4 +// CHECK-NEXT: br label %expand.end +// CHECK: expand.end: +// CHECK-NEXT: br label %expand.next5 +// CHECK: expand.next5: +// CHECK-NEXT: %9 = load i32, ptr getelementptr inbounds (i32, ptr @_ZZ2f4vE1a, i64 1), align 4 +// CHECK-NEXT: store i32 %9, ptr %x6, align 4 +// CHECK-NEXT: store ptr @_ZZ2f4vE1b, ptr %__range27, align 8 +// CHECK-NEXT: store ptr @_ZZ2f4vE1b, ptr %__begin28, align 8 +// CHECK-NEXT: store ptr getelementptr (i8, ptr @_ZZ2f4vE1b, i64 8), ptr %__end29, align 8 +// CHECK-NEXT: %10 = load i32, ptr @_ZZ2f4vE1b, align 4 +// CHECK-NEXT: store i32 %10, ptr %y10, align 4 +// CHECK-NEXT: %11 = load i32, ptr %x6, align 4 +// CHECK-NEXT: %12 = load i32, ptr %y10, align 4 +// CHECK-NEXT: %add11 = add nsw i32 %11, %12 +// CHECK-NEXT: %13 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %add12 = add nsw i32 %13, %add11 +// CHECK-NEXT: store i32 %add12, ptr %sum, align 4 +// CHECK-NEXT: br label %expand.next13 +// CHECK: expand.next13: +// CHECK-NEXT: %14 = load i32, ptr getelementptr inbounds (i32, ptr @_ZZ2f4vE1b, i64 1), align 4 +// CHECK-NEXT: store i32 %14, ptr %y14, align 4 +// CHECK-NEXT: %15 = load i32, ptr %x6, align 4 +// CHECK-NEXT: %16 = load i32, ptr %y14, align 4 +// CHECK-NEXT: %add15 = add nsw i32 %15, %16 +// CHECK-NEXT: %17 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %add16 = add nsw i32 %17, %add15 +// CHECK-NEXT: store i32 %add16, ptr %sum, align 4 +// CHECK-NEXT: br label %expand.end17 +// CHECK: expand.end17: +// CHECK-NEXT: br label %expand.end18 +// CHECK: expand.end18: +// CHECK-NEXT: store ptr @_ZZ2f4vE1a, ptr %__range119, align 8 +// CHECK-NEXT: store ptr @_ZZ2f4vE1a, ptr %__begin120, align 8 +// CHECK-NEXT: store ptr getelementptr (i8, ptr @_ZZ2f4vE1a, i64 8), ptr %__end121, align 8 +// CHECK-NEXT: store i32 1, ptr %x22, align 4 +// CHECK-NEXT: store ptr @_ZZ2f4vE1b, ptr %__range223, align 8 +// CHECK-NEXT: store ptr @_ZZ2f4vE1b, ptr %__begin224, align 8 +// CHECK-NEXT: store ptr getelementptr (i8, ptr @_ZZ2f4vE1b, i64 8), ptr %__end225, align 8 +// CHECK-NEXT: store i32 3, ptr %y26, align 4 +// CHECK-NEXT: %18 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %add27 = add nsw i32 %18, 4 +// CHECK-NEXT: store i32 %add27, ptr %sum, align 4 +// CHECK-NEXT: br label %expand.next28 +// CHECK: expand.next28: +// CHECK-NEXT: store i32 4, ptr %y29, align 4 +// CHECK-NEXT: %19 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %add30 = add nsw i32 %19, 5 +// CHECK-NEXT: store i32 %add30, ptr %sum, align 4 +// CHECK-NEXT: br label %expand.end31 +// CHECK: expand.end31: +// CHECK-NEXT: br label %expand.next32 +// CHECK: expand.next32: +// CHECK-NEXT: store i32 2, ptr %x33, align 4 +// CHECK-NEXT: store ptr @_ZZ2f4vE1b, ptr %__range234, align 8 +// CHECK-NEXT: store ptr @_ZZ2f4vE1b, ptr %__begin235, align 8 +// CHECK-NEXT: store ptr getelementptr (i8, ptr @_ZZ2f4vE1b, i64 8), ptr %__end236, align 8 +// CHECK-NEXT: store i32 3, ptr %y37, align 4 +// CHECK-NEXT: %20 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %add38 = add nsw i32 %20, 5 +// CHECK-NEXT: store i32 %add38, ptr %sum, align 4 +// CHECK-NEXT: br label %expand.next39 +// CHECK: expand.next39: +// CHECK-NEXT: store i32 4, ptr %y40, align 4 +// CHECK-NEXT: %21 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %add41 = add nsw i32 %21, 6 +// CHECK-NEXT: store i32 %add41, ptr %sum, align 4 +// CHECK-NEXT: br label %expand.end42 +// CHECK: expand.end42: +// CHECK-NEXT: br label %expand.end43 +// CHECK: expand.end43: +// CHECK-NEXT: %22 = load i32, ptr %sum, align 4 +// CHECK-NEXT: ret i32 %22 + + +// CHECK-LABEL: define {{.*}} i32 @_ZN7Private11member_funcEv() +// CHECK: entry: +// CHECK-NEXT: %sum = alloca i32, align 4 +// CHECK-NEXT: %__range1 = alloca ptr, align 8 +// CHECK-NEXT: %__begin1 = alloca ptr, align 8 +// CHECK-NEXT: %__end1 = alloca ptr, align 8 +// CHECK-NEXT: %x = alloca i32, align 4 +// CHECK-NEXT: %x1 = alloca i32, align 4 +// CHECK-NEXT: %x4 = alloca i32, align 4 +// CHECK-NEXT: store i32 0, ptr %sum, align 4 +// CHECK-NEXT: store ptr @_ZZN7Private11member_funcEvE2p1, ptr %__range1, align 8 +// CHECK-NEXT: store ptr @_ZN7Private8integersE, ptr %__begin1, align 8 +// CHECK-NEXT: store ptr getelementptr (i8, ptr @_ZN7Private8integersE, i64 12), ptr %__end1, align 8 +// CHECK-NEXT: %0 = load i32, ptr @_ZN7Private8integersE, align 4 +// CHECK-NEXT: store i32 %0, ptr %x, align 4 +// CHECK-NEXT: %1 = load i32, ptr %x, align 4 +// CHECK-NEXT: %2 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %add = add nsw i32 %2, %1 +// CHECK-NEXT: store i32 %add, ptr %sum, align 4 +// CHECK-NEXT: br label %expand.next +// CHECK: expand.next: +// CHECK-NEXT: %3 = load i32, ptr getelementptr inbounds (i32, ptr @_ZN7Private8integersE, i64 1), align 4 +// CHECK-NEXT: store i32 %3, ptr %x1, align 4 +// CHECK-NEXT: %4 = load i32, ptr %x1, align 4 +// CHECK-NEXT: %5 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %add2 = add nsw i32 %5, %4 +// CHECK-NEXT: store i32 %add2, ptr %sum, align 4 +// CHECK-NEXT: br label %expand.next3 +// CHECK: expand.next3: +// CHECK-NEXT: %6 = load i32, ptr getelementptr inbounds (i32, ptr @_ZN7Private8integersE, i64 2), align 4 +// CHECK-NEXT: store i32 %6, ptr %x4, align 4 +// CHECK-NEXT: %7 = load i32, ptr %x4, align 4 +// CHECK-NEXT: %8 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %add5 = add nsw i32 %8, %7 +// CHECK-NEXT: store i32 %add5, ptr %sum, align 4 +// CHECK-NEXT: br label %expand.end +// CHECK: expand.end: +// CHECK-NEXT: %9 = load i32, ptr %sum, align 4 +// CHECK-NEXT: ret i32 %9 + + +// CHECK-LABEL: define {{.*}} i32 @_Z15custom_iteratorv() +// CHECK: entry: +// CHECK-NEXT: %sum = alloca i32, align 4 +// CHECK-NEXT: %__range1 = alloca ptr, align 8 +// CHECK: %__begin1 = alloca %"struct.CustomIterator::iterator", align 4 +// CHECK: %__end1 = alloca %"struct.CustomIterator::iterator", align 4 +// CHECK-NEXT: %x = alloca i32, align 4 +// CHECK: %ref.tmp = alloca %"struct.CustomIterator::iterator", align 4 +// CHECK-NEXT: %x2 = alloca i32, align 4 +// CHECK: %ref.tmp3 = alloca %"struct.CustomIterator::iterator", align 4 +// CHECK-NEXT: %x9 = alloca i32, align 4 +// CHECK: %ref.tmp10 = alloca %"struct.CustomIterator::iterator", align 4 +// CHECK-NEXT: %x16 = alloca i32, align 4 +// CHECK: %ref.tmp17 = alloca %"struct.CustomIterator::iterator", align 4 +// CHECK-NEXT: %__range122 = alloca ptr, align 8 +// CHECK: %__begin123 = alloca %"struct.CustomIterator::iterator", align 4 +// CHECK: %__end124 = alloca %"struct.CustomIterator::iterator", align 4 +// CHECK-NEXT: %x25 = alloca i32, align 4 +// CHECK-NEXT: %x28 = alloca i32, align 4 +// CHECK-NEXT: %x31 = alloca i32, align 4 +// CHECK-NEXT: %x34 = alloca i32, align 4 +// CHECK-NEXT: store i32 0, ptr %sum, align 4 +// CHECK-NEXT: store ptr @_ZZ15custom_iteratorvE1c, ptr %__range1, align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %__begin1, ptr align 4 @__const._Z15custom_iteratorv.__begin1, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %__end1, ptr align 4 @__const._Z15custom_iteratorv.__end1, i64 4, i1 false) +// CHECK-NEXT: %call = call i32 @_ZNK14CustomIterator8iteratorplEi(ptr {{.*}} %__begin1, i32 {{.*}} 0) +// CHECK: %coerce.dive = getelementptr inbounds nuw %"struct.CustomIterator::iterator", ptr %ref.tmp, i32 0, i32 0 +// CHECK-NEXT: store i32 %call, ptr %coerce.dive, align 4 +// CHECK-NEXT: %call1 = call {{.*}} i32 @_ZNK14CustomIterator8iteratordeEv(ptr {{.*}} %ref.tmp) +// CHECK-NEXT: store i32 %call1, ptr %x, align 4 +// CHECK-NEXT: %0 = load i32, ptr %x, align 4 +// CHECK-NEXT: %1 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %add = add nsw i32 %1, %0 +// CHECK-NEXT: store i32 %add, ptr %sum, align 4 +// CHECK-NEXT: br label %expand.next +// CHECK: expand.next: +// CHECK-NEXT: %call4 = call i32 @_ZNK14CustomIterator8iteratorplEi(ptr {{.*}} %__begin1, i32 {{.*}} 1) +// CHECK: %coerce.dive5 = getelementptr inbounds nuw %"struct.CustomIterator::iterator", ptr %ref.tmp3, i32 0, i32 0 +// CHECK-NEXT: store i32 %call4, ptr %coerce.dive5, align 4 +// CHECK-NEXT: %call6 = call {{.*}} i32 @_ZNK14CustomIterator8iteratordeEv(ptr {{.*}} %ref.tmp3) +// CHECK-NEXT: store i32 %call6, ptr %x2, align 4 +// CHECK-NEXT: %2 = load i32, ptr %x2, align 4 +// CHECK-NEXT: %3 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %add7 = add nsw i32 %3, %2 +// CHECK-NEXT: store i32 %add7, ptr %sum, align 4 +// CHECK-NEXT: br label %expand.next8 +// CHECK: expand.next8: +// CHECK-NEXT: %call11 = call i32 @_ZNK14CustomIterator8iteratorplEi(ptr {{.*}} %__begin1, i32 {{.*}} 2) +// CHECK: %coerce.dive12 = getelementptr inbounds nuw %"struct.CustomIterator::iterator", ptr %ref.tmp10, i32 0, i32 0 +// CHECK-NEXT: store i32 %call11, ptr %coerce.dive12, align 4 +// CHECK-NEXT: %call13 = call {{.*}} i32 @_ZNK14CustomIterator8iteratordeEv(ptr {{.*}} %ref.tmp10) +// CHECK-NEXT: store i32 %call13, ptr %x9, align 4 +// CHECK-NEXT: %4 = load i32, ptr %x9, align 4 +// CHECK-NEXT: %5 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %add14 = add nsw i32 %5, %4 +// CHECK-NEXT: store i32 %add14, ptr %sum, align 4 +// CHECK-NEXT: br label %expand.next15 +// CHECK: expand.next15: +// CHECK-NEXT: %call18 = call i32 @_ZNK14CustomIterator8iteratorplEi(ptr {{.*}} %__begin1, i32 {{.*}} 3) +// CHECK: %coerce.dive19 = getelementptr inbounds nuw %"struct.CustomIterator::iterator", ptr %ref.tmp17, i32 0, i32 0 +// CHECK-NEXT: store i32 %call18, ptr %coerce.dive19, align 4 +// CHECK-NEXT: %call20 = call {{.*}} i32 @_ZNK14CustomIterator8iteratordeEv(ptr {{.*}} %ref.tmp17) +// CHECK-NEXT: store i32 %call20, ptr %x16, align 4 +// CHECK-NEXT: %6 = load i32, ptr %x16, align 4 +// CHECK-NEXT: %7 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %add21 = add nsw i32 %7, %6 +// CHECK-NEXT: store i32 %add21, ptr %sum, align 4 +// CHECK-NEXT: br label %expand.end +// CHECK: expand.end: +// CHECK-NEXT: store ptr @_ZZ15custom_iteratorvE1c, ptr %__range122, align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %__begin123, ptr align 4 @__const._Z15custom_iteratorv.__begin1.1, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %__end124, ptr align 4 @__const._Z15custom_iteratorv.__end1.2, i64 4, i1 false) +// CHECK-NEXT: store i32 1, ptr %x25, align 4 +// CHECK-NEXT: %8 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %add26 = add nsw i32 %8, 1 +// CHECK-NEXT: store i32 %add26, ptr %sum, align 4 +// CHECK-NEXT: br label %expand.next27 +// CHECK: expand.next27: +// CHECK-NEXT: store i32 2, ptr %x28, align 4 +// CHECK-NEXT: %9 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %add29 = add nsw i32 %9, 2 +// CHECK-NEXT: store i32 %add29, ptr %sum, align 4 +// CHECK-NEXT: br label %expand.next30 +// CHECK: expand.next30: +// CHECK-NEXT: store i32 3, ptr %x31, align 4 +// CHECK-NEXT: %10 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %add32 = add nsw i32 %10, 3 +// CHECK-NEXT: store i32 %add32, ptr %sum, align 4 +// CHECK-NEXT: br label %expand.next33 +// CHECK: expand.next33: +// CHECK-NEXT: store i32 4, ptr %x34, align 4 +// CHECK-NEXT: %11 = load i32, ptr %sum, align 4 +// CHECK-NEXT: %add35 = add nsw i32 %11, 4 +// CHECK-NEXT: store i32 %add35, ptr %sum, align 4 +// CHECK-NEXT: br label %expand.end36 +// CHECK: expand.end36: +// CHECK-NEXT: %12 = load i32, ptr %sum, align 4 +// CHECK-NEXT: ret i32 %12 diff --git a/clang/test/Parser/cxx2c-expansion-statements.cpp b/clang/test/Parser/cxx2c-expansion-statements.cpp new file mode 100644 index 0000000000000..4c594a111dee6 --- /dev/null +++ b/clang/test/Parser/cxx2c-expansion-statements.cpp @@ -0,0 +1,62 @@ +// RUN: %clang_cc1 %s -std=c++2c -fsyntax-only -verify +namespace std { +template +struct initializer_list { + const T* a; + const T* b; + initializer_list(T*, T*) {} +}; +} + +void bad() { + template for; // expected-error {{expected '(' after 'for'}} + template for (); // expected-error {{expected expression}} expected-error {{expected ';' in 'for' statement specifier}} expected-error {{expansion statement must be a range-based for loop}} + template for (;); // expected-error {{expected ';' in 'for' statement specifier}} expected-error {{expansion statement must be a range-based for loop}} + template for (;;); // expected-error {{expansion statement must be a range-based for loop}} + template for (int x;;); // expected-error {{expansion statement must be a range-based for loop}} + template for (x : {1}); // expected-error {{expansion statement requires type for expansion variable}} + template for (: {1}); // expected-error {{expected expression}} expected-error {{expected ';' in 'for' statement specifier}} expected-error {{expansion statement must be a range-based for loop}} + template for (auto y : {1})]; // expected-error {{expected expression}} + template for (auto y : {1}; // expected-error {{expected ')'}} expected-note {{to match this '('}} + + template for (extern auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'extern'}} + template for (extern static auto y : {1, 2}); // expected-error {{cannot combine with previous 'extern' declaration specifier}} expected-error {{expansion variable 'y' may not be declared 'extern'}} + template for (static auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'static'}} + template for (thread_local auto y : {1, 2}); // expected-error {{'thread_local' variables must have global storage}} + template for (static thread_local auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'thread_local'}} + template for (__thread auto y : {1, 2}); // expected-error {{'__thread' variables must have global storage}} + template for (static __thread auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'static'}} + template for (constinit auto y : {1, 2}); // expected-error {{local variable cannot be declared 'constinit'}} + template for (consteval auto y : {1, 2}); // expected-error {{consteval can only be used in function declarations}} + template for (int x; extern auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'extern'}} + template for (int x; extern static auto y : {1, 2}); // expected-error {{cannot combine with previous 'extern' declaration specifier}} expected-error {{expansion variable 'y' may not be declared 'extern'}} + template for (int x; static auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'static'}} + template for (int x; thread_local auto y : {1, 2}); // expected-error {{'thread_local' variables must have global storage}} + template for (int x; static thread_local auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'thread_local'}} + template for (int x; __thread auto y : {1, 2}); // expected-error {{'__thread' variables must have global storage}} + template for (int x; static __thread auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'static'}} + template for (int x; constinit auto y : {1, 2}); // expected-error {{local variable cannot be declared 'constinit'}} + template for (int x; consteval auto y : {1, 2}); // expected-error {{consteval can only be used in function declarations}} + template for (auto y : {abc, -+, }); // expected-error {{use of undeclared identifier 'abc'}} expected-error {{expected expression}} + template while (true) {} // expected-error {{expected '<' after 'template'}} + template for (auto y : {{1}, {2}, {3, {4}}, {{{5}}}}); +} + +void good() { + template for (auto y : {}); + template for (auto y : {1, 2}); + template for (int x; auto y : {1, 2}); + template for (int x; int y : {1, 2}); + template for (int x; constexpr auto y : {1, 2}); + template for (int x; constexpr int y : {1, 2}); + template for (constexpr int a : {1, 2}) { + template for (constexpr int b : {1, 2}) { + template for (constexpr int c : {1, 2}); + } + } +} + +void trailing_comma() { + template for (int x : {1, 2,}) {} + template for (int x : {,}) {} // expected-error {{expected expression}} +} diff --git a/clang/test/SemaCXX/cxx2c-expansion-statements.cpp b/clang/test/SemaCXX/cxx2c-expansion-statements.cpp new file mode 100644 index 0000000000000..61825914f90dd --- /dev/null +++ b/clang/test/SemaCXX/cxx2c-expansion-statements.cpp @@ -0,0 +1,1096 @@ +// RUN: %clang_cc1 %s -std=c++2c -fsyntax-only -fdeclspec -fblocks -fexpansion-limit=32 -verify +namespace std { +template +struct initializer_list { + const T* a; + const T* b; + initializer_list(T* a, T* b): a{a}, b{b} {} +}; +} + +struct S { + int x; + constexpr S(int x) : x{x} {} +}; + +void g(int); // #g +template constexpr int tg() { return n; } + +void f1() { + template for (auto x : {}) static_assert(false, "discarded"); + template for (constexpr auto x : {}) static_assert(false, "discarded"); + template for (auto x : {1}) g(x); + template for (auto x : {1, 2, 3}) g(x); + template for (constexpr auto x : {1}) g(x); + template for (constexpr auto x : {1, 2, 3}) g(x); + template for (constexpr auto x : {1}) tg(); + template for (constexpr auto x : {1, 2, 3}) + static_assert(tg()); + + template for (int x : {1, 2, 3}) g(x); + template for (S x : {1, 2, 3}) g(x.x); + template for (constexpr S x : {1, 2, 3}) tg(); + + template for (int x : {"1", S(1), {1, 2}}) { // expected-error {{cannot initialize a variable of type 'int' with an lvalue of type 'const char[2]'}} \ + expected-error {{no viable conversion from 'S' to 'int'}} \ + expected-error {{excess elements in scalar initializer}} \ + expected-note 3 {{in instantiation of expansion statement requested here}} + g(x); + } + + template for (constexpr auto x : {1, 2, 3, 4}) { // expected-note 3 {{in instantiation of expansion statement requested here}} + static_assert(tg() == 4); // expected-error 3 {{static assertion failed due to requirement 'tg() == 4'}} \ + expected-note {{expression evaluates to '1 == 4'}} \ + expected-note {{expression evaluates to '2 == 4'}} \ + expected-note {{expression evaluates to '3 == 4'}} + } + + + template for (constexpr auto x : {1, 2}) { // expected-note 2 {{in instantiation of expansion statement requested here}} + static_assert(false, "not discarded"); // expected-error 2 {{static assertion failed: not discarded}} + } +} + +template +void t1() { + template for (T x : {}) g(x); + template for (constexpr T x : {}) g(x); + template for (auto x : {}) g(x); + template for (constexpr auto x : {}) g(x); + template for (T x : {1, 2}) g(x); + template for (T x : {T(1), T(2)}) g(x); + template for (auto x : {T(1), T(2)}) g(x); + template for (constexpr T x : {T(1), T(2)}) static_assert(tg()); + template for (constexpr auto x : {T(1), T(2)}) static_assert(tg()); +} + +template +struct s1 { + template + void tf() { + template for (T x : {}) g(x); + template for (constexpr T x : {}) g(x); + template for (U x : {}) g(x); + template for (constexpr U x : {}) g(x); + template for (auto x : {}) g(x); + template for (constexpr auto x : {}) g(x); + template for (T x : {1, 2}) g(x); + template for (U x : {1, 2}) g(x); + template for (U x : {T(1), T(2)}) g(x); + template for (T x : {U(1), U(2)}) g(x); + template for (auto x : {T(1), T(2)}) g(x); + template for (auto x : {U(1), T(2)}) g(x); + template for (constexpr U x : {T(1), T(2)}) static_assert(tg()); + template for (constexpr T x : {U(1), U(2)}) static_assert(tg()); + template for (constexpr auto x : {T(1), U(2)}) static_assert(tg()); + } +}; + +template +void t2() { + template for (T x : {}) g(x); +} + +void f2() { + t1(); + t1(); + s1().tf(); + s1().tf(); + s1().tf(); + s1().tf(); + t2(); + t2(); + t2(); +} + +template <__SIZE_TYPE__ size> +struct String { + char data[size]; + + template <__SIZE_TYPE__ n> + constexpr String(const char (&str)[n]) { __builtin_memcpy(data, str, n); } + + constexpr const char* begin() const { return data; } + constexpr const char* end() const { return data + size - 1; } +}; + +template <__SIZE_TYPE__ n> +String(const char (&str)[n]) -> String; + +constexpr int f3() { + static constexpr String s{"abcd"}; + int count = 0; + template for (constexpr auto x : s) count++; + return count; +} + +template +constexpr int tf3() { + int count = 0; + template for (constexpr auto x : s) count++; + return count; +} + +static_assert(f3() == 4); +static_assert(tf3<"1">() == 1); +static_assert(tf3<"12">() == 2); +static_assert(tf3<"123">() == 3); +static_assert(tf3<"1234">() == 4); + +void f4() { + static constexpr String empty{""}; + static constexpr String s{"abcd"}; + template for (auto x : empty) static_assert(false, "not expanded"); + template for (constexpr auto x : s) g(x); + template for (auto x : s) g(x); +} + +struct NegativeSize { + static constexpr const char* str = "123"; + constexpr const char* begin() const { return str + 3; } + constexpr const char* end() const { return str; } +}; + +template +struct Array { + T data[size]{}; + constexpr const T* begin() const { return data; } + constexpr const T* end() const { return data + size; } +}; + +void expansion_size() { + static constexpr Array almost_too_big; + template for (auto x : almost_too_big) g(x); + template for (constexpr auto x : almost_too_big) g(x); + + static constexpr Array too_big; + template for (auto x : too_big) g(x); // expected-error {{expansion size 33 exceeds maximum configured size 32}} expected-note {{use -fexpansion-limit=N to adjust this limit}} + template for (constexpr auto x : too_big) g(x); // expected-error {{expansion size 33 exceeds maximum configured size 32}} expected-note {{use -fexpansion-limit=N to adjust this limit}} + + static constexpr String big{"1234567890123456789012345678901234567890234567890"}; + template for (auto x : big) g(x); // expected-error {{expansion size 49 exceeds maximum configured size 32}} expected-note {{use -fexpansion-limit=N to adjust this limit}} + template for (constexpr auto x : big) g(x); // expected-error {{expansion size 49 exceeds maximum configured size 32}} expected-note {{use -fexpansion-limit=N to adjust this limit}} + + static constexpr NegativeSize n; + template for (auto x : n) g(x); // expected-error {{expansion size must not be negative (was -3)}} + template for (constexpr auto x : n) g(x); // expected-error {{expansion size must not be negative (was -3)}} + + template for (auto x : {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, + 31, 32}) g(x); + template for (constexpr auto x : {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, + 31, 32}) g(x); + + template for (auto x : {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, // expected-error {{expansion size 33 exceeds maximum configured size 32}} expected-note {{use -fexpansion-limit=N to adjust this limit}} + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, + 31, 32, 33}) g(x); + template for (constexpr auto x : {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, // expected-error {{expansion size 33 exceeds maximum configured size 32}} expected-note {{use -fexpansion-limit=N to adjust this limit}} + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, + 31, 32, 33}) g(x); + + int huge[1'000'000'000]; + template for (auto x : huge) {} // expected-error {{expansion size 1000000000 exceeds maximum configured size 32}} expected-note {{use -fexpansion-limit=N to adjust this limit}} +} + +struct NotInt { + struct iterator {}; + constexpr iterator begin() const { return {}; } + constexpr iterator end() const { return {}; } +}; + +void not_int() { + static constexpr NotInt ni; + template for (auto x : ni) g(x); // expected-error {{invalid operands to binary expression}} +} + +static constexpr Array integers{1, 2, 3}; + +constexpr int friend_func(); + +struct Private { + friend constexpr int friend_func(); + +private: + constexpr const int* begin() const { return integers.begin(); } // expected-note 2 {{declared private here}} + constexpr const int* end() const { return integers.end(); } // expected-note 2 {{declared private here}} + +public: + static constexpr int member_func() { + int sum = 0; + static constexpr Private p1; + template for (auto x : p1) sum += x; + return sum; + } +}; + +struct Protected { + friend constexpr int friend_func(); + +protected: + constexpr const int* begin() const { return integers.begin(); } // expected-note 2 {{declared protected here}} + constexpr const int* end() const { return integers.end(); } // expected-note 2 {{declared protected here}} + +public: + static constexpr int member_func() { + int sum = 0; + static constexpr Protected p1; + template for (auto x : p1) sum += x; + return sum; + } +}; + +void access_control() { + static constexpr Private p1; + template for (auto x : p1) g(x); // expected-error 2 {{'begin' is a private member of 'Private'}} expected-error 2 {{'end' is a private member of 'Private'}} + + static constexpr Protected p2; + template for (auto x : p2) g(x); // expected-error 2 {{'begin' is a protected member of 'Protected'}} expected-error 2 {{'end' is a protected member of 'Protected'}} +} + +constexpr int friend_func() { + int sum = 0; + static constexpr Private p1; + template for (auto x : p1) sum += x; + + static constexpr Protected p2; + template for (auto x : p2) sum += x; + return sum; +} + +static_assert(friend_func() == 12); +static_assert(Private::member_func() == 6); +static_assert(Protected::member_func() == 6); + +struct SizeNotICE { + struct iterator { + friend constexpr iterator operator+(iterator a, __PTRDIFF_TYPE__) { return a; } + int constexpr operator*() const { return 7; } + + // NOT constexpr! + friend int operator-(iterator, iterator) { return 7; } // expected-note {{declared here}} + friend int operator!=(iterator, iterator) { return 7; } + }; + constexpr iterator begin() const { return {}; } + constexpr iterator end() const { return {}; } +}; + +struct PlusMissing { + struct iterator { + int constexpr operator*() const { return 7; } + }; + constexpr iterator begin() const { return {}; } + constexpr iterator end() const { return {}; } +}; + +struct DerefMissing { + struct iterator { + friend constexpr iterator operator+(iterator a, __PTRDIFF_TYPE__) { return a; } + }; + constexpr iterator begin() const { return {}; } + constexpr iterator end() const { return {}; } +}; + +void missing_funcs() { + static constexpr SizeNotICE s1; + static constexpr PlusMissing s2; + static constexpr DerefMissing s3; + + // TODO: This message should start complaining about '!=' once we support the + // proper way of computing the size. + template for (auto x : s1) g(x); // expected-error {{expansion size is not a constant expression}} \ + expected-note {{non-constexpr function 'operator-' cannot be used in a constant expression}} + + template for (auto x : s2) g(x); // expected-error {{invalid operands to binary expression}} + template for (auto x : s3) g(x); // expected-error {{indirection requires pointer operand ('iterator' invalid)}} +} + +namespace adl { +struct ADL { + +}; + +constexpr const int* begin(const ADL&) { return integers.begin(); } +constexpr const int* end(const ADL&) { return integers.end(); } +} + +namespace adl_error { +struct ADLError1 { + constexpr const int* begin() const { return integers.begin(); } // expected-note {{member is not a candidate because range type 'const adl_error::ADLError1' has no 'end' member}} +}; + +struct ADLError2 { + constexpr const int* end() const { return integers.end(); } // expected-note {{member is not a candidate because range type 'const adl_error::ADLError2' has no 'begin' member}} +}; + +constexpr const int* begin(const ADLError2&) { return integers.begin(); } // expected-note {{candidate function not viable: no known conversion from 'const adl_error::ADLError1' to 'const ADLError2' for 1st argument}} +constexpr const int* end(const ADLError1&) { return integers.end(); } // expected-note {{candidate function not viable: no known conversion from 'const adl_error::ADLError2' to 'const ADLError1' for 1st argument}} +} + +namespace adl_both { +static constexpr Array integers2{1, 2, 3, 4, 5}; +struct ADLBoth { + // Test that member begin/end are preferred over ADl begin/end. These return + // pointers to a different array. + constexpr const int* begin() const { return integers2.begin(); } + constexpr const int* end() const { return integers2.end(); } +}; + +constexpr const int* begin(const ADLBoth&) { return integers.begin(); } +constexpr const int* end(const ADLBoth&) { return integers.end(); } +} + +constexpr int adl_begin_end() { + static constexpr adl::ADL a; + int sum = 0; + template for (auto x : a) sum += x; + template for (constexpr auto x : a) sum += x; + return sum; +} + +static_assert(adl_begin_end() == 12); + +void adl_mixed_error() { + static constexpr adl_error::ADLError1 a1; + static constexpr adl_error::ADLError2 a2; + template for (auto x : a1) g(x); // expected-error {{invalid range expression of type 'const adl_error::ADLError1'; no viable 'begin' function available}} + template for (auto x : a2) g(x); // expected-error {{invalid range expression of type 'const adl_error::ADLError2'; no viable 'end' function available}} +} + +constexpr int adl_both_test() { + static constexpr adl_both::ADLBoth a; + int sum = 0; + template for (auto x : a) sum += x; + return sum; +} + +static_assert(adl_both_test() == 15); + +struct A {}; +struct B { int x = 1; }; +struct C { int a = 1, b = 2, c = 3; }; +struct D { + int a = 1; + int* b = nullptr; + const char* c = "3"; +}; + +struct Nested { + A a; + B b; + C c; +}; + +struct PrivateDestructurable { + friend void destructurable_friend(); +private: + int a, b; // expected-note 4 {{declared private here}} +}; + +struct ProtectedDestructurable { + friend void destructurable_friend(); +protected: + int a, b; // expected-note 4 {{declared protected here}} +}; + +void destructuring() { + static constexpr A a; + static constexpr B b; + static constexpr C c; + static constexpr D d; + + template for (auto x : a) static_assert(false, "not expanded"); + template for (constexpr auto x : a) static_assert(false, "not expanded"); + + template for (auto x : b) g(x); + template for (constexpr auto x : b) g(x); + + template for (auto x : c) g(x); + template for (constexpr auto x : c) g(x); + + template for (auto x : d) { // expected-note 2 {{in instantiation of expansion statement requested here}} + // expected-note@#g {{candidate function not viable: no known conversion from 'int *' to 'int' for 1st argument}} + // expected-note@#g {{candidate function not viable: no known conversion from 'const char *' to 'int' for 1st argument}} + g(x); // expected-error 2 {{no matching function for call to 'g'}} + + } + + template for (constexpr auto x : d) { // expected-note 2 {{in instantiation of expansion statement requested here}} + // expected-note@#g {{candidate function not viable: no known conversion from 'int *const' to 'int' for 1st argument}} + // expected-note@#g {{candidate function not viable: no known conversion from 'const char *const' to 'int' for 1st argument}} + g(x); // expected-error 2 {{no matching function for call to 'g'}} + } +} + +constexpr int array() { + static constexpr int x[4]{1, 2, 3, 4}; + int sum = 0; + template for (auto y : x) sum += y; + template for (constexpr auto y : x) sum += y; + return sum; +} + +static_assert(array() == 20); + +void array_too_big() { + int ok[32]; + int too_big[33]; + + template for (auto x : ok) {} + template for (auto x : too_big) {} // expected-error {{expansion size 33 exceeds maximum configured size 32}} \ + expected-note {{use -fexpansion-limit=N to adjust this limit}} +} + +template +constexpr int destructure() { + int sum = 0; + template for (auto x : v) sum += x; + template for (constexpr auto x : v) sum += x; + return sum; +} + +static_assert(destructure() == 20); +static_assert(destructure() == 12); +static_assert(destructure() == 24); + +constexpr int nested() { + static constexpr Nested n; + int sum = 0; + template for (constexpr auto x : n) { + static constexpr auto val = x; + template for (auto y : val) { + sum += y; + } + } + template for (constexpr auto x : n) { + static constexpr auto val = x; + template for (constexpr auto y : val) { + sum += y; + } + } + return sum; +} + +static_assert(nested() == 14); + +void access_control_destructurable() { + template for (auto x : PrivateDestructurable()) {} // expected-error 2 {{cannot bind private member 'a' of 'PrivateDestructurable'}} \ + expected-error 2 {{cannot bind private member 'b' of 'PrivateDestructurable'}} + + template for (auto x : ProtectedDestructurable()) {} // expected-error 2 {{cannot bind protected member 'a' of 'ProtectedDestructurable'}} \ + expected-error 2 {{cannot bind protected member 'b' of 'ProtectedDestructurable'}} +} + +void destructurable_friend() { + template for (auto x : PrivateDestructurable()) {} + template for (auto x : ProtectedDestructurable()) {} +} + +struct Placeholder { + A get_value() const { return {}; } + __declspec(property(get = get_value)) A a; +}; + +void placeholder() { + template for (auto x: Placeholder().a) {} +} + +union Union { int a; long b;}; + +struct MemberPtr { + void f() {} +}; + +void overload_set(int); // expected-note 2 {{possible target for call}} +void overload_set(long); // expected-note 2 {{possible target for call}} + +void invalid_types() { + template for (auto x : void()) {} // expected-error {{cannot expand expression of type 'void'}} + template for (auto x : 1) {} // expected-error {{cannot expand expression of type 'int'}} + template for (auto x : 1.f) {} // expected-error {{cannot expand expression of type 'float'}} + template for (auto x : 'c') {} // expected-error {{cannot expand expression of type 'char'}} + template for (auto x : invalid_types) {} // expected-error {{cannot expand expression of type 'void ()'}} + template for (auto x : &invalid_types) {} // expected-error {{cannot expand expression of type 'void (*)()'}} + template for (auto x : &MemberPtr::f) {} // expected-error {{cannot expand expression of type 'void (MemberPtr::*)()'}} + template for (auto x : overload_set) {} // expected-error{{reference to overloaded function could not be resolved; did you mean to call it?}} + template for (auto x : &overload_set) {} // expected-error{{reference to overloaded function could not be resolved; did you mean to call it?}} + template for (auto x : nullptr) {} // expected-error {{cannot expand expression of type 'std::nullptr_t'}} + template for (auto x : __builtin_strlen) {} // expected-error {{builtin functions must be directly called}} + template for (auto x : Union()) {} // expected-error {{cannot expand expression of type 'Union'}} + template for (auto x : (char*)nullptr) {} // expected-error {{cannot expand expression of type 'char *'}} + template for (auto x : []{}) {} // expected-error {{cannot expand lambda closure type}} + template for (auto x : [x=3]{}) {} // expected-error {{cannot expand lambda closure type}} +} + +struct BeginOnly { + int x{1}; + constexpr const int* begin() const { return nullptr; } +}; + +struct EndOnly { + int x{2}; + constexpr const int* end() const { return nullptr; } +}; + +namespace adl1 { +struct BeginOnly { + int x{3}; +}; +constexpr const int* begin(const BeginOnly&) { return nullptr; } +} + +namespace adl2 { +struct EndOnly { + int x{4}; +}; +constexpr const int* end(const EndOnly&) { return nullptr; } +} + +constexpr int unpaired_begin_end() { + static constexpr BeginOnly b1; + static constexpr EndOnly e1; + static constexpr adl1::BeginOnly b2; + static constexpr adl2::EndOnly e2; + int sum = 0; + + template for (auto x : b1) sum += x; + template for (auto x : e1) sum += x; + + template for (auto x : b2) sum += x; + template for (auto x : e2) sum += x; + + return sum; +} + +static_assert(unpaired_begin_end() == 10); + +// Examples taken from [stmt.expand]. +namespace stmt_expand_examples { +consteval int f(auto const&... Containers) { + int result = 0; + template for (auto const& c : {Containers...}) { // OK, enumerating expansion statement + result += c[0]; + } + return result; +} +constexpr int c1[] = {1, 2, 3}; +constexpr int c2[] = {4, 3, 2, 1}; +static_assert(f(c1, c2) == 5); + +// TODO: This entire example should work without issuing any diagnostics once +// we have full support for references to constexpr variables (P2686). +consteval int f() { + constexpr Array arr {1, 2, 3}; // expected-note{{add 'static' to give it a constant address}} + + int result = 0; + + // expected-error@#invalid-ref {{constexpr variable '__range1' must be initialized by a constant expression}} + // expected-error@#invalid-ref {{constexpr variable '__begin1' must be initialized by a constant expression}} + // expected-error@#invalid-ref {{constexpr variable '__end1' must be initialized by a constant expression}} + // expected-error@#invalid-ref {{expansion size is not a constant expression}} + // expected-note@#invalid-ref 2 {{member call on variable '__range1' whose value is not known}} + // expected-note@#invalid-ref 1 {{initializer of '__end1' is not a constant expression}} + // expected-note@#invalid-ref 3 {{declared here}} + // expected-note@#invalid-ref {{reference to 'arr' is not a constant expression}} + template for (constexpr int s : arr) { // #invalid-ref // OK, iterating expansion statement + result += sizeof(char[s]); + } + return result; +} +static_assert(f() == 6); // expected-error {{static assertion failed due to requirement 'f() == 6'}} expected-note {{expression evaluates to '0 == 6'}} + +struct S { + int i; + short s; +}; + +consteval long f(S s) { + long result = 0; + template for (auto x : s) { // OK, destructuring expansion statement + result += sizeof(x); + } + return result; +} +static_assert(f(S{}) == sizeof(int) + sizeof(short)); +} + +void not_constant_expression() { + template for (constexpr auto x : B()) { // expected-error {{constexpr variable '[__u0]' must be initialized by a constant expression}} \ + expected-note {{reference to temporary is not a constant expression}} \ + expected-note {{temporary created here}} \ + expected-error {{constexpr variable 'x' must be initialized by a constant expression}} \ + expected-note {{in instantiation of expansion statement requested here}} \ + expected-note {{read of variable '[__u0]' whose value is not known}} \ + expected-note {{declared here}} + g(x); + } +} + +constexpr int references_enumerating() { + int x = 1, y = 2, z = 3; + template for (auto& x : {x, y, z}) { ++x; } + template for (auto&& x : {x, y, z}) { ++x; } + return x + y + z; +} + +static_assert(references_enumerating() == 12); + +constexpr int references_destructuring() { + C c; + template for (auto& x : c) { ++x; } + template for (auto&& x : c) { ++x; } + return c.a + c.b + c.c; +} + +static_assert(references_destructuring() == 12); + +constexpr int break_continue() { + int sum = 0; + template for (auto x : {1, 2}) { + break; + sum += x; + } + + template for (auto x : {3, 4}) { + continue; + sum += x; + } + + template for (auto x : {5, 6}) { + if (x == 6) break; + sum += x; + } + + template for (auto x : {7, 8, 9}) { + if (x == 8) continue; + sum += x; + } + + return sum; +} + +static_assert(break_continue() == 21); + +constexpr int break_continue_nested() { + int sum = 0; + + template for (auto x : {1, 2}) { + template for (auto y : {3, 4}) { + if (x == 2) break; + sum += y; + } + sum += x; + } + + template for (auto x : {5, 6}) { + template for (auto y : {7, 8}) { + if (x == 6) continue; + sum += y; + } + sum += x; + } + + return sum; +} + +static_assert(break_continue_nested() == 36); + + +void label() { + template for (auto x : {1, 2}) { + invalid1:; // expected-error {{labels are not allowed in expansion statements}} + invalid2:; // expected-error {{labels are not allowed in expansion statements}} + goto invalid1; // expected-error {{use of undeclared label 'invalid1'}} + } + + template for (auto x : {1, 2}) { + (void) [] { + template for (auto x : {1, 2}) { + invalid3:; // expected-error {{labels are not allowed in expansion statements}} + } + ok:; + }; + + (void) ^{ + template for (auto x : {1, 2}) { + invalid4:; // expected-error {{labels are not allowed in expansion statements}} + } + ok:; + }; + + struct X { + void f() { + ok:; + } + }; + } + + // GNU local labels are allowed. + template for (auto x : {1, 2}) { + __label__ a; + if (x == 1) goto a; + a:; + if (x == 1) goto a; + } + + // Likewise, jumping *out* of an expansion statement is fine. + template for (auto x : {1, 2}) { + if (x == 1) goto lbl; + g(x); + } + lbl:; + template for (auto x : {1, 2}) { + if (x == 1) goto lbl; + g(x); + } + + // Jumping into one is not possible, as local labels aren't visible + // outside the block that declares them, and non-local labels are invalid. + goto exp1; // expected-error {{use of undeclared label 'exp1'}} + goto exp3; // expected-error {{use of undeclared label 'exp3'}} + template for (auto x : {1, 2}) { + __label__ exp1, exp2; + exp1:; + exp2:; + exp3:; // expected-error {{labels are not allowed in expansion statements}} + } + goto exp2; // expected-error {{use of undeclared label 'exp2'}} + + // Allow jumping from inside an expansion statement to a local label in + // one of its parents. + out1:; + template for (auto x : {1, 2}) { + __label__ x, y; + x: + goto out1; + goto out2; + template for (auto x : {3, 4}) { + goto x; + goto y; + goto out1; + goto out2; + } + y: + } + out2:; +} + + +void case_default(int i) { + switch (i) { // expected-note 3 {{switch statement is here}} + template for (auto x : {1, 2}) { + case 1:; // expected-error {{'case' belongs to 'switch' outside enclosing expansion statement}} + template for (auto x : {1, 2}) { + case 2:; // expected-error {{'case' belongs to 'switch' outside enclosing expansion statement}} + } + default: // expected-error {{'default' belongs to 'switch' outside enclosing expansion statement}} + switch (i) { // expected-note {{switch statement is here}} + case 3:; + default: + template for (auto x : {1, 2}) { + case 4:; // expected-error {{'case' belongs to 'switch' outside enclosing expansion statement}} + } + } + } + } + + template for (auto x : {1, 2}) { + switch (i) { + case 1:; + default: + } + } + + // Ensure that we diagnose this even if the statements would be discarded. + switch (i) { // expected-note 2 {{switch statement is here}} + template for (auto x : {}) { + case 1:; // expected-error {{'case' belongs to 'switch' outside enclosing expansion statement}} + default:; // expected-error {{'default' belongs to 'switch' outside enclosing expansion statement}} + } + } +} + +template +void unexpanded_pack_bad(Ts ...ts) { + template for (auto x : ts) {} // expected-error {{expression contains unexpanded parameter pack 'ts'}} + template for (Ts x : {1, 2}) {} // expected-error {{declaration type contains unexpanded parameter pack 'Ts'}} + template for (auto x : {ts}) {} // expected-error {{initializer contains unexpanded parameter pack}} \ + // expected-note {{in instantiation of expansion statement requested here}} +} + +struct E { int x, y; constexpr E(int x, int y) : x{x}, y{y} {}}; + +template +constexpr int unexpanded_pack_good(Es ...es) { + int sum = 0; + ([&] { + template for (auto x : es) sum += x; + template for (Es e : {{5, 6}, {7, 8}}) sum += e.x + e.y; + }(), ...); + return sum; +} + +static_assert(unexpanded_pack_good(E{1, 2}, E{3, 4}) == 62); + +// Ensure that the expansion-initializer is evaluated even if it expands +// to nothing. +// +// This is related to CWG 3048. Note that we currently still model this as +// a DecompositionDecl w/ zero bindings. +constexpr bool empty_side_effect() { + struct A { + constexpr A(bool& b) { + b = true; + } + }; + + bool constructed = false; + template for (auto x : A(constructed)) static_assert(false); + return constructed; +} + +static_assert(empty_side_effect()); + +namespace apply_lifetime_extension { +struct T { + int& x; + constexpr T(int& x) noexcept : x(x) {} + constexpr ~T() noexcept { x = 42; } +}; + +constexpr const T& f(const T& t) noexcept { return t; } +constexpr T g(int& x) noexcept { return T(x); } + +// CWG 3043: +// +// Lifetime extension only applies to destructuring expansion statements +// (enumerating statements don't have a range variable, and the range variable +// of iterating statements is constexpr). +constexpr int lifetime_extension() { + int x = 5; + int sum = 0; + template for (auto e : f(g(x))) { + sum += x; + } + return sum + x; +} + +template +constexpr int lifetime_extension_instantiate_expansions() { + int x = 5; + int sum = 0; + template for (T e : f(g(x))) { + sum += x; + } + return sum + x; +} + +template +constexpr int lifetime_extension_dependent_expansion_stmt() { + int x = 5; + int sum = 0; + template for (int e : f(g((T&)x))) { + sum += x; + } + return sum + x; +} + +template +struct foo { + template + constexpr int lifetime_extension_multiple_instantiations() { + int x = 5; + int sum = 0; + template for (T e : f(g((U&)x))) { + sum += x; + } + return sum + x; + } +}; + +static_assert(lifetime_extension() == 47); +static_assert(lifetime_extension_instantiate_expansions() == 47); +static_assert(lifetime_extension_dependent_expansion_stmt() == 47); +static_assert(foo().lifetime_extension_multiple_instantiations() == 47); +} + +template +constexpr int return_from_expansion(Ts... ts) { + template for (int i : {1, 2, 3}) { + return (ts + ...); + } + __builtin_unreachable(); +} + +static_assert(return_from_expansion(4, 5, 6) == 15); + +void not_constexpr(); + +constexpr int empty_expansion_consteval() { + template for (auto _ : {}) { + not_constexpr(); + } + return 3; +} + +static_assert(empty_expansion_consteval() == 3); + +void nested_empty_expansion() { + template for (auto x1 : {}) + template for (auto x2 : {1}) + static_assert(false); + + template for (auto x1 : {1}) + template for (auto x2 : {}) + template for (auto x3 : {1}) + static_assert(false); + + template for (auto x1 : {}) + template for (auto x2 : {}) + template for (auto x3 : {}) + template for (auto x4 : {1}) + static_assert(false); + + template for (auto x1 : {}) + template for (auto x2 : {1}) + template for (auto x3 : {}) + template for (auto x4 : {1}) + static_assert(false); + + template for (auto x1 : {}) + template for (auto x2 : {1}) + template for (auto x4 : {1}) + static_assert(false); +} + +struct Empty {}; + +template +void nested_empty_expansion_dependent() { + template for (auto x1 : T()) + template for (auto x2 : {1}) + static_assert(false); + + template for (auto x1 : {1}) + template for (auto x2 : T()) + template for (auto x3 : {1}) + static_assert(false); + + template for (auto x1 : T()) + template for (auto x2 : T()) + template for (auto x3 : T()) + template for (auto x4 : {1}) + static_assert(false); + + template for (auto x1 : T()) + template for (auto x2 : {1}) + template for (auto x3 : T()) + template for (auto x4 : {1}) + static_assert(false); + + template for (auto x1 : T()) + template for (auto x2 : {1}) + template for (auto x4 : {1}) + static_assert(false); +} + +void nested_empty_expansion_dependent_instantiate() { + nested_empty_expansion_dependent(); +} + +// Destructuring expansion statements using tuple_size/tuple_element/get. +namespace std { +template +struct tuple_size; + +template <__SIZE_TYPE__, typename> +struct tuple_element; // expected-note {{template is declared here}} + +namespace get_decomposition { +struct MemberGet { + int x[6]{}; + + template <__SIZE_TYPE__ I> + constexpr int& get() { return x[I * 2]; } +}; + +struct ADLGet { + long x[8]{}; +}; + +template <__SIZE_TYPE__ I> +constexpr long& get(ADLGet& a) { return a.x[I * 2]; } +} // namespace get_decomposition + +template <> +struct tuple_size { + static constexpr __SIZE_TYPE__ value = 3; +}; + +template <__SIZE_TYPE__ I> +struct tuple_element { + using type = int; +}; + +template <> +struct tuple_size { + static constexpr __SIZE_TYPE__ value = 4; +}; + +template <__SIZE_TYPE__ I> +struct tuple_element { + using type = long; +}; + +constexpr int member() { + get_decomposition::MemberGet m; + int v = 1; + template for (int& i : m) { + i = v; + v++; + } + return m.x[0] + m.x[2] + m.x[4]; +} + +constexpr long adl() { + get_decomposition::ADLGet m; + long v = 1; + template for (long& i : m) { + i = v; + v++; + } + return m.x[0] + m.x[2] + m.x[4] + m.x[6]; +} + +static_assert(member() == 6); +static_assert(adl() == 10); + +struct TupleSizeOnly {}; + +template <> +struct tuple_size { + static constexpr __SIZE_TYPE__ value = 3; +}; + +struct TupleSizeAndGet { + template <__SIZE_TYPE__> + constexpr int get() { return 1; } +}; + +template <> +struct tuple_size { + static constexpr __SIZE_TYPE__ value = 3; +}; + +void invalid() { + template for (auto x : TupleSizeOnly()) {} // expected-error {{use of undeclared identifier 'get'}} \ + expected-note {{in implicit initialization of binding declaration}} + + template for (auto x : TupleSizeAndGet()) {} // expected-error {{implicit instantiation of undefined template 'std::tuple_element<0, std::TupleSizeAndGet>'}} \ + expected-note {{in implicit initialization of binding declaration}} +} +} // namespace std diff --git a/clang/test/SemaCXX/cxx2c-expansion-stmt-ext-warn.cpp b/clang/test/SemaCXX/cxx2c-expansion-stmt-ext-warn.cpp new file mode 100644 index 0000000000000..13bee4371793a --- /dev/null +++ b/clang/test/SemaCXX/cxx2c-expansion-stmt-ext-warn.cpp @@ -0,0 +1,8 @@ +// RUN: %clang_cc1 %s -std=c++2c -fsyntax-only -verify=cxx26 -Wpre-c++26-compat +// RUN: %clang_cc1 %s -std=c++23 -fsyntax-only -verify=cxx23 + +void f() { + template for (auto _ : {1}) { // cxx23-warning {{expansion statements are a C++2c extension}} \ + // cxx26-warning {{expansion statements are incompatible with C++ standards before C++2c}} + } +} diff --git a/clang/test/SemaCXX/cxx2c-fexpansion-statements.cpp b/clang/test/SemaCXX/cxx2c-fexpansion-statements.cpp new file mode 100644 index 0000000000000..2c80c392e400d --- /dev/null +++ b/clang/test/SemaCXX/cxx2c-fexpansion-statements.cpp @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 %s -std=c++2c -fsyntax-only -fexpansion-limit=0 -verify +// expected-no-diagnostics + +// Test that passing =0 disables the limit. + +void big() { + int ok[500]; + template for (auto x : ok) {} +} diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp index fc27fd29da933..ac7dbd908fea7 100644 --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -7247,6 +7247,7 @@ CXCursor clang_getCursorDefinition(CXCursor C) { case Decl::UnresolvedUsingIfExists: case Decl::OpenACCDeclare: case Decl::OpenACCRoutine: + case Decl::ExpansionStmt: 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..c9cdaa6ee1285 100644 --- a/clang/tools/libclang/CXCursor.cpp +++ b/clang/tools/libclang/CXCursor.cpp @@ -290,6 +290,11 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent, case Stmt::CoroutineBodyStmtClass: case Stmt::CoreturnStmtClass: + case Stmt::CXXEnumeratingExpansionStmtClass: + case Stmt::CXXIteratingExpansionStmtClass: + case Stmt::CXXDestructuringExpansionStmtClass: + case Stmt::CXXDependentExpansionStmtClass: + case Stmt::CXXExpansionInstantiationStmtClass: K = CXCursor_UnexposedStmt; break; @@ -338,6 +343,9 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent, case Stmt::EmbedExprClass: case Stmt::HLSLOutArgExprClass: case Stmt::OpenACCAsteriskSizeExprClass: + case Stmt::CXXExpansionInitListExprClass: + case Stmt::CXXExpansionInitListSelectExprClass: + case Stmt::CXXDestructuringExpansionSelectExprClass: K = CXCursor_UnexposedExpr; break; diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html index 2618ff930a0e4..0bb2d5440775a 100755 --- a/clang/www/cxx_status.html +++ b/clang/www/cxx_status.html @@ -317,7 +317,7 @@

C++2c implementation status

Expansion Statements P1306R5 - No + Clang 22 constexpr virtual inheritance