diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index 67ee0bb412692..bdf6c81732d0b 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -698,6 +698,10 @@ class ValueDecl : public NamedDecl { return const_cast<ValueDecl *>(this)->getPotentiallyDecomposedVarDecl(); } + /// Determine whether this value is actually a function parameter pack, + /// init-capture pack, or structured binding pack + bool isParameterPack() const; + // Implement isa/cast/dyncast/etc. static bool classof(const Decl *D) { return classofKind(D->getKind()); } static bool classofKind(Kind K) { return K >= firstValue && K <= lastValue; } @@ -1527,10 +1531,6 @@ class VarDecl : public DeclaratorDecl, public Redeclarable<VarDecl> { NonParmVarDeclBits.IsInitCapture = IC; } - /// Determine whether this variable is actually a function parameter pack or - /// init-capture pack. - bool isParameterPack() const; - /// Whether this local extern variable declaration's previous declaration /// was declared in the same block scope. Only correct in C++. bool isPreviousDeclInSameBlockScope() const { diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h index c232556edeff7..0d092bb816439 100644 --- a/clang/include/clang/AST/DeclCXX.h +++ b/clang/include/clang/AST/DeclCXX.h @@ -4131,8 +4131,9 @@ class BindingDecl : public ValueDecl { /// binding). Expr *Binding = nullptr; - BindingDecl(DeclContext *DC, SourceLocation IdLoc, IdentifierInfo *Id) - : ValueDecl(Decl::Binding, DC, IdLoc, Id, QualType()) {} + BindingDecl(DeclContext *DC, SourceLocation IdLoc, IdentifierInfo *Id, + QualType T) + : ValueDecl(Decl::Binding, DC, IdLoc, Id, T) {} void anchor() override; @@ -4140,7 +4141,8 @@ class BindingDecl : public ValueDecl { friend class ASTDeclReader; static BindingDecl *Create(ASTContext &C, DeclContext *DC, - SourceLocation IdLoc, IdentifierInfo *Id); + SourceLocation IdLoc, IdentifierInfo *Id, + QualType T); static BindingDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID); /// Get the expression to which this declaration is bound. This may be null @@ -4148,14 +4150,13 @@ class BindingDecl : public ValueDecl { /// decomposition declaration, and when the initializer is type-dependent. Expr *getBinding() const { return Binding; } + // Get the array of Exprs when the binding represents a pack. + llvm::ArrayRef<Expr *> getBindingPackExprs() const; + /// Get the decomposition declaration that this binding represents a /// decomposition of. ValueDecl *getDecomposedDecl() const { return Decomp; } - /// Get the variable (if any) that holds the value of evaluating the binding. - /// Only present for user-defined bindings for tuple-like types. - VarDecl *getHoldingVar() const; - /// Set the binding for this BindingDecl, along with its declared type (which /// should be a possibly-cv-qualified form of the type of the binding, or a /// reference to such a type). @@ -4167,6 +4168,10 @@ class BindingDecl : public ValueDecl { /// Set the decomposed variable for this BindingDecl. void setDecomposedDecl(ValueDecl *Decomposed) { Decomp = Decomposed; } + /// Get the variable (if any) that holds the value of evaluating the binding. + /// Only present for user-defined bindings for tuple-like types. + VarDecl *getHoldingVar() const; + static bool classof(const Decl *D) { return classofKind(D->getKind()); } static bool classofKind(Kind K) { return K == Decl::Binding; } }; @@ -4194,8 +4199,16 @@ class DecompositionDecl final NumBindings(Bindings.size()) { std::uninitialized_copy(Bindings.begin(), Bindings.end(), getTrailingObjects<BindingDecl *>()); - for (auto *B : Bindings) + for (auto *B : Bindings) { B->setDecomposedDecl(this); + if (B->isParameterPack() && B->getBinding()) { + for (Expr *E : B->getBindingPackExprs()) { + auto *DRE = cast<DeclRefExpr>(E); + auto *NestedB = cast<BindingDecl>(DRE->getDecl()); + NestedB->setDecomposedDecl(this); + } + } + } } void anchor() override; @@ -4213,8 +4226,33 @@ class DecompositionDecl final static DecompositionDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID, unsigned NumBindings); - ArrayRef<BindingDecl *> bindings() const { - return llvm::ArrayRef(getTrailingObjects<BindingDecl *>(), NumBindings); + // Provide the range of bindings which may have a nested pack. + llvm::ArrayRef<BindingDecl *> bindings() const { + return {getTrailingObjects<BindingDecl *>(), NumBindings}; + } + + // Provide a flattened range to visit each binding. + auto flat_bindings() const { + llvm::ArrayRef<BindingDecl *> Bindings = bindings(); + llvm::ArrayRef<Expr *> PackExprs; + + // Split the bindings into subranges split by the pack. + auto S1 = Bindings.take_until( + [](BindingDecl *BD) { return BD->isParameterPack(); }); + + Bindings = Bindings.drop_front(S1.size()); + if (!Bindings.empty()) { + PackExprs = Bindings.front()->getBindingPackExprs(); + Bindings = Bindings.drop_front(); + } + + auto S2 = llvm::map_range(PackExprs, [](Expr *E) { + auto *DRE = cast<DeclRefExpr>(E); + return cast<BindingDecl>(DRE->getDecl()); + }); + + return llvm::concat<BindingDecl *>(std::move(S1), std::move(S2), + std::move(Bindings)); } void printName(raw_ostream &OS, const PrintingPolicy &Policy) const override; diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h index 4cec89c979f77..09d10087add71 100644 --- a/clang/include/clang/AST/ExprCXX.h +++ b/clang/include/clang/AST/ExprCXX.h @@ -5321,6 +5321,59 @@ class BuiltinBitCastExpr final } }; +// Represents an unexpanded pack where the list of expressions are +// known. These are used when structured bindings introduce a pack. +class ResolvedUnexpandedPackExpr final + : public Expr, + private llvm::TrailingObjects<ResolvedUnexpandedPackExpr, Expr *> { + friend class ASTStmtReader; + friend class ASTStmtWriter; + friend TrailingObjects; + + SourceLocation BeginLoc; + unsigned NumExprs; + + ResolvedUnexpandedPackExpr(SourceLocation BL, QualType QT, unsigned NumExprs); + +public: + static ResolvedUnexpandedPackExpr *CreateDeserialized(ASTContext &C, + unsigned NumExprs); + static ResolvedUnexpandedPackExpr * + Create(ASTContext &C, SourceLocation BeginLoc, QualType T, unsigned NumExprs); + static ResolvedUnexpandedPackExpr *Create(ASTContext &C, + SourceLocation BeginLoc, QualType T, + llvm::ArrayRef<Expr *> Exprs); + + unsigned getNumExprs() const { return NumExprs; } + + llvm::MutableArrayRef<Expr *> getExprs() { + return {getTrailingObjects<Expr *>(), NumExprs}; + } + + llvm::ArrayRef<Expr *> getExprs() const { + return {getTrailingObjects<Expr *>(), NumExprs}; + } + + Expr *getExpansion(unsigned Idx) { return getExprs()[Idx]; } + Expr *getExpansion(unsigned Idx) const { return getExprs()[Idx]; } + + // Iterators + child_range children() { + return child_range((Stmt **)getTrailingObjects<Expr *>(), + (Stmt **)getTrailingObjects<Expr *>() + getNumExprs()); + } + + SourceLocation getBeginLoc() const LLVM_READONLY { return BeginLoc; } + SourceLocation getEndLoc() const LLVM_READONLY { return BeginLoc; } + + // Returns the resolved pack of a decl or nullptr + static ResolvedUnexpandedPackExpr *getFromDecl(Decl *); + + static bool classof(const Stmt *T) { + return T->getStmtClass() == ResolvedUnexpandedPackExprClass; + } +}; + } // 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 f5b32ed51698e..9f8a8f2a8348f 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -2936,6 +2936,7 @@ DEF_TRAVERSE_STMT(FunctionParmPackExpr, {}) DEF_TRAVERSE_STMT(CXXFoldExpr, {}) DEF_TRAVERSE_STMT(AtomicExpr, {}) DEF_TRAVERSE_STMT(CXXParenListInitExpr, {}) +DEF_TRAVERSE_STMT(ResolvedUnexpandedPackExpr, {}) DEF_TRAVERSE_STMT(MaterializeTemporaryExpr, { if (S->getLifetimeExtendedTemporaryDecl()) { diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index 86fcae209c40d..fbd1d0bce9ade 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -1099,6 +1099,16 @@ def err_lambda_capture_misplaced_ellipsis : Error< "the name of the capture">; def err_lambda_capture_multiple_ellipses : Error< "multiple ellipses in pack capture">; +def err_binding_multiple_ellipses : Error< + "multiple packs in structured binding declaration">; +def note_previous_ellipsis : Note< + "previous binding pack specified here">; +def ext_cxx_binding_pack : ExtWarn< + "structured binding packs are a C++2c extension ">, + InGroup<CXX26>; +def warn_cxx23_compat_binding_pack : Warning< + "structured binding packs are incompatible with C++ standards before C++2c">, + InGroup<CXXPre26Compat>, DefaultIgnore; def err_capture_default_first : Error< "capture default must be first">; def ext_decl_attrs_on_lambda : ExtWarn< diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 330ae045616ab..22012fb266353 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -5906,6 +5906,9 @@ def warn_cxx23_pack_indexing : Warning< "pack indexing is incompatible with C++ standards before C++2c">, DefaultIgnore, InGroup<CXXPre26Compat>; +def err_pack_outside_template : Error< + "pack declaration outside of template">; + def err_fold_expression_packs_both_sides : Error< "binary fold expression has unexpanded parameter packs in both operands">; def err_fold_expression_empty : Error< diff --git a/clang/include/clang/Basic/StmtNodes.td b/clang/include/clang/Basic/StmtNodes.td index 31280df93e4c6..a5ac8eba371f2 100644 --- a/clang/include/clang/Basic/StmtNodes.td +++ b/clang/include/clang/Basic/StmtNodes.td @@ -162,6 +162,7 @@ def MaterializeTemporaryExpr : StmtNode<Expr>; def LambdaExpr : StmtNode<Expr>; def CXXFoldExpr : StmtNode<Expr>; def CXXParenListInitExpr: StmtNode<Expr>; +def ResolvedUnexpandedPackExpr : StmtNode<Expr>; // C++ Coroutines expressions def CoroutineSuspendExpr : StmtNode<Expr, 1>; diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h index 06243f2624876..5f5df3a45d41d 100644 --- a/clang/include/clang/Sema/DeclSpec.h +++ b/clang/include/clang/Sema/DeclSpec.h @@ -1795,6 +1795,7 @@ class DecompositionDeclarator { IdentifierInfo *Name; SourceLocation NameLoc; std::optional<ParsedAttributes> Attrs; + SourceLocation EllipsisLoc; }; private: diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 5ee7ea48cc983..24e13278cb2a8 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -230,7 +230,8 @@ void threadSafetyCleanup(BeforeSet *Cache); // FIXME: No way to easily map from TemplateTypeParmTypes to // TemplateTypeParmDecls, so we have this horrible PointerUnion. -typedef std::pair<llvm::PointerUnion<const TemplateTypeParmType *, NamedDecl *>, +typedef std::pair<llvm::PointerUnion<const TemplateTypeParmType *, NamedDecl *, + ResolvedUnexpandedPackExpr *>, SourceLocation> UnexpandedParameterPack; @@ -6012,6 +6013,7 @@ class Sema final : public SemaBase { RecordDecl *ClassDecl, const IdentifierInfo *Name); + unsigned GetDecompositionElementCount(QualType DecompType); void CheckCompleteDecompositionDeclaration(DecompositionDecl *DD); /// Stack containing information needed when in C++2a an 'auto' is encountered diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index dfd82afad4007..bab63be73e58e 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -1890,6 +1890,7 @@ enum StmtCode { EXPR_PACK_EXPANSION, // PackExpansionExpr EXPR_PACK_INDEXING, // PackIndexingExpr EXPR_SIZEOF_PACK, // SizeOfPackExpr + EXPR_RESOLVED_UNEXPANDED_PACK, // ResolvedUnexpandedPackExpr EXPR_SUBST_NON_TYPE_TEMPLATE_PARM, // SubstNonTypeTemplateParmExpr EXPR_SUBST_NON_TYPE_TEMPLATE_PARM_PACK, // SubstNonTypeTemplateParmPackExpr EXPR_FUNCTION_PARM_PACK, // FunctionParmPackExpr diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 8b4ae58e8427a..c340d6aeebc60 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -12726,11 +12726,12 @@ bool ASTContext::DeclMustBeEmitted(const Decl *D) { // Likewise, variables with tuple-like bindings are required if their // bindings have side-effects. - if (const auto *DD = dyn_cast<DecompositionDecl>(VD)) - for (const auto *BD : DD->bindings()) + if (const auto *DD = dyn_cast<DecompositionDecl>(VD)) { + for (const auto *BD : DD->flat_bindings()) if (const auto *BindingVD = BD->getHoldingVar()) if (DeclMustBeEmitted(BindingVD)) return true; + } return false; } diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index 26d33b0d94795..33b4fe0b8fecc 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -2552,7 +2552,7 @@ ExpectedDecl ASTNodeImporter::VisitBindingDecl(BindingDecl *D) { BindingDecl *ToD; if (GetImportedOrCreateDecl(ToD, D, Importer.getToContext(), DC, Loc, - Name.getAsIdentifierInfo())) + Name.getAsIdentifierInfo(), D->getType())) return ToD; Error Err = Error::success(); diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 741e908cf9bc5..76c208bef6031 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -2659,10 +2659,6 @@ bool VarDecl::checkForConstantInitialization( return Eval->HasConstantInitialization; } -bool VarDecl::isParameterPack() const { - return isa<PackExpansionType>(getType()); -} - template<typename DeclT> static DeclT *getDefinitionOrSelf(DeclT *D) { assert(D); @@ -5397,6 +5393,13 @@ bool ValueDecl::isInitCapture() const { return false; } +bool ValueDecl::isParameterPack() const { + if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(this)) + return NTTP->isParameterPack(); + + return isa_and_nonnull<PackExpansionType>(getType().getTypePtrOrNull()); +} + void ImplicitParamDecl::anchor() {} ImplicitParamDecl *ImplicitParamDecl::Create(ASTContext &C, DeclContext *DC, diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp index fb701f76231bc..29bb3c0b6816b 100644 --- a/clang/lib/AST/DeclBase.cpp +++ b/clang/lib/AST/DeclBase.cpp @@ -237,7 +237,7 @@ bool Decl::isTemplateParameterPack() const { } bool Decl::isParameterPack() const { - if (const auto *Var = dyn_cast<VarDecl>(this)) + if (const auto *Var = dyn_cast<ValueDecl>(this)) return Var->isParameterPack(); return isTemplateParameterPack(); diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp index af73c658d6a0c..06b84aafce320 100644 --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -3395,19 +3395,21 @@ VarDecl *ValueDecl::getPotentiallyDecomposedVarDecl() { if (auto *Var = llvm::dyn_cast<VarDecl>(this)) return Var; if (auto *BD = llvm::dyn_cast<BindingDecl>(this)) - return llvm::dyn_cast<VarDecl>(BD->getDecomposedDecl()); + return llvm::dyn_cast_if_present<VarDecl>(BD->getDecomposedDecl()); return nullptr; } void BindingDecl::anchor() {} BindingDecl *BindingDecl::Create(ASTContext &C, DeclContext *DC, - SourceLocation IdLoc, IdentifierInfo *Id) { - return new (C, DC) BindingDecl(DC, IdLoc, Id); + SourceLocation IdLoc, IdentifierInfo *Id, + QualType T) { + return new (C, DC) BindingDecl(DC, IdLoc, Id, T); } BindingDecl *BindingDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID) { - return new (C, ID) BindingDecl(nullptr, SourceLocation(), nullptr); + return new (C, ID) + BindingDecl(nullptr, SourceLocation(), nullptr, QualType()); } VarDecl *BindingDecl::getHoldingVar() const { @@ -3423,6 +3425,12 @@ VarDecl *BindingDecl::getHoldingVar() const { return VD; } +llvm::ArrayRef<Expr *> BindingDecl::getBindingPackExprs() const { + assert(Binding && "expecting a pack expr"); + auto *RP = cast<ResolvedUnexpandedPackExpr>(Binding); + return RP->getExprs(); +} + void DecompositionDecl::anchor() {} DecompositionDecl *DecompositionDecl::Create(ASTContext &C, DeclContext *DC, diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index 8c8ccdb61dc01..26a227814388f 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -3653,6 +3653,7 @@ bool Expr::HasSideEffects(const ASTContext &Ctx, case PackIndexingExprClass: case HLSLOutArgExprClass: case OpenACCAsteriskSizeExprClass: + case ResolvedUnexpandedPackExprClass: // These never have a side-effect. return false; diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp index fc09d24fc30cb..a1620376d1a40 100644 --- a/clang/lib/AST/ExprCXX.cpp +++ b/clang/lib/AST/ExprCXX.cpp @@ -1965,3 +1965,52 @@ CXXFoldExpr::CXXFoldExpr(QualType T, UnresolvedLookupExpr *Callee, SubExprs[SubExpr::RHS] = RHS; setDependence(computeDependence(this)); } + +ResolvedUnexpandedPackExpr::ResolvedUnexpandedPackExpr(SourceLocation BL, + QualType QT, + unsigned NumExprs) + : Expr(ResolvedUnexpandedPackExprClass, QT, VK_PRValue, OK_Ordinary), + BeginLoc(BL), NumExprs(NumExprs) { + // C++ [temp.dep.expr]p3 + // An id-expression is type-dependent if it is + // - associated by name lookup with a pack + setDependence(ExprDependence::TypeValueInstantiation | + ExprDependence::UnexpandedPack); +} + +ResolvedUnexpandedPackExpr * +ResolvedUnexpandedPackExpr::CreateDeserialized(ASTContext &Ctx, + unsigned NumExprs) { + void *Mem = Ctx.Allocate(totalSizeToAlloc<Expr *>(NumExprs), + alignof(ResolvedUnexpandedPackExpr)); + return new (Mem) + ResolvedUnexpandedPackExpr(SourceLocation(), QualType(), NumExprs); +} + +ResolvedUnexpandedPackExpr * +ResolvedUnexpandedPackExpr::Create(ASTContext &Ctx, SourceLocation BL, + QualType T, unsigned NumExprs) { + void *Mem = Ctx.Allocate(totalSizeToAlloc<Expr *>(NumExprs), + alignof(ResolvedUnexpandedPackExpr)); + ResolvedUnexpandedPackExpr *New = + new (Mem) ResolvedUnexpandedPackExpr(BL, T, NumExprs); + + auto Exprs = New->getExprs(); + std::uninitialized_fill(Exprs.begin(), Exprs.end(), nullptr); + + return New; +} + +ResolvedUnexpandedPackExpr * +ResolvedUnexpandedPackExpr::Create(ASTContext &Ctx, SourceLocation BL, + QualType T, ArrayRef<Expr *> Exprs) { + auto *New = Create(Ctx, BL, T, Exprs.size()); + std::uninitialized_copy(Exprs.begin(), Exprs.end(), New->getExprs().begin()); + return New; +} + +ResolvedUnexpandedPackExpr *ResolvedUnexpandedPackExpr::getFromDecl(Decl *D) { + if (auto *BD = dyn_cast<BindingDecl>(D)) + return dyn_cast_if_present<ResolvedUnexpandedPackExpr>(BD->getBinding()); + return nullptr; +} diff --git a/clang/lib/AST/ExprClassification.cpp b/clang/lib/AST/ExprClassification.cpp index 3f37d06cc8f3a..5225c3ca773ad 100644 --- a/clang/lib/AST/ExprClassification.cpp +++ b/clang/lib/AST/ExprClassification.cpp @@ -451,6 +451,13 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { case Expr::PackExpansionExprClass: return ClassifyInternal(Ctx, cast<PackExpansionExpr>(E)->getPattern()); + case Expr::ResolvedUnexpandedPackExprClass: { + if (cast<ResolvedUnexpandedPackExpr>(E)->getNumExprs() > 0) + return ClassifyInternal( + Ctx, cast<ResolvedUnexpandedPackExpr>(E)->getExpansion(0)); + return Cl::CL_LValue; + } + case Expr::MaterializeTemporaryExprClass: return cast<MaterializeTemporaryExpr>(E)->isBoundToLvalueReference() ? Cl::CL_LValue diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index dd75dca647540..c767ea9306584 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -5155,7 +5155,7 @@ static bool EvaluateDecl(EvalInfo &Info, const Decl *D) { OK &= EvaluateVarDecl(Info, VD); if (const DecompositionDecl *DD = dyn_cast<DecompositionDecl>(D)) - for (auto *BD : DD->bindings()) + for (auto *BD : DD->flat_bindings()) if (auto *VD = BD->getHoldingVar()) OK &= EvaluateDecl(Info, VD); @@ -17122,6 +17122,7 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) { case Expr::SYCLUniqueStableNameExprClass: case Expr::CXXParenListInitExprClass: case Expr::HLSLOutArgExprClass: + case Expr::ResolvedUnexpandedPackExprClass: return ICEDiag(IK_NotICE, E->getBeginLoc()); case Expr::InitListExprClass: { diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index 47aa9b40dab84..ffa9703536b7f 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -4926,7 +4926,7 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity, case Expr::SourceLocExprClass: case Expr::EmbedExprClass: case Expr::BuiltinBitCastExprClass: - { + case Expr::ResolvedUnexpandedPackExprClass: { NotPrimaryExpr(); if (!NullOut) { // As bad as this diagnostic is, it's better than crashing. diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index c5d19f70fc6ea..64955553108f7 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -44,9 +44,11 @@ #include "clang/Basic/TypeTraits.h" #include "clang/Lex/Lexer.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Support/Casting.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" @@ -2556,6 +2558,15 @@ void StmtPrinter::VisitPackIndexingExpr(PackIndexingExpr *E) { OS << "]"; } +void StmtPrinter::VisitResolvedUnexpandedPackExpr( + ResolvedUnexpandedPackExpr *E) { + OS << "<<resolved pack("; + llvm::interleave( + E->getExprs().begin(), E->getExprs().end(), + [this](auto *X) { PrintExpr(X); }, [this] { OS << ", "; }); + OS << ")>>"; +} + void StmtPrinter::VisitSubstNonTypeTemplateParmPackExpr( SubstNonTypeTemplateParmPackExpr *Node) { OS << *Node->getParameterPack(); diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp index 27313f9ae1275..1424f03ebf78b 100644 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -2276,6 +2276,10 @@ void StmtProfiler::VisitSizeOfPackExpr(const SizeOfPackExpr *S) { ID.AddInteger(0); } } +void StmtProfiler::VisitResolvedUnexpandedPackExpr( + const ResolvedUnexpandedPackExpr *S) { + VisitExpr(S); +} void StmtProfiler::VisitPackIndexingExpr(const PackIndexingExpr *E) { VisitExpr(E); diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp index f29ddece5dbc9..df23ce6d48cd7 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -5067,10 +5067,9 @@ CGDebugInfo::EmitDeclareOfAutoVariable(const VarDecl *VD, llvm::Value *Storage, assert(CGM.getCodeGenOpts().hasReducedDebugInfo()); if (auto *DD = dyn_cast<DecompositionDecl>(VD)) { - for (auto *B : DD->bindings()) { + for (BindingDecl *B : DD->flat_bindings()) EmitDeclare(B, Storage, std::nullopt, Builder, VD->getType()->isReferenceType()); - } // Don't emit an llvm.dbg.declare for the composite storage as it doesn't // correspond to a user variable. return nullptr; diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp index 47b21bc9f63f0..55f6b7c882c91 100644 --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -163,9 +163,10 @@ void CodeGenFunction::EmitDecl(const Decl &D) { "Should not see file-scope variables inside a function!"); EmitVarDecl(VD); if (auto *DD = dyn_cast<DecompositionDecl>(&VD)) - for (auto *B : DD->bindings()) + for (auto *B : DD->flat_bindings()) if (auto *HD = B->getHoldingVar()) EmitVarDecl(*HD); + return; } diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index c49f763148828..a5bd54dd41e6b 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -6987,9 +6987,10 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) { case Decl::VarTemplateSpecialization: EmitGlobal(cast<VarDecl>(D)); if (auto *DD = dyn_cast<DecompositionDecl>(D)) - for (auto *B : DD->bindings()) + for (auto *B : DD->flat_bindings()) if (auto *HD = B->getHoldingVar()) EmitGlobal(HD); + break; // Indirect fields from global anonymous structs and unions can be diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 937a94b02458c..aba963c5abd1d 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -7308,15 +7308,16 @@ void Parser::ParseDecompositionDeclarator(Declarator &D) { // If this doesn't look like a structured binding, maybe it's a misplaced // array declarator. - if (!(Tok.is(tok::identifier) && + if (!(Tok.isOneOf(tok::identifier, tok::ellipsis) && NextToken().isOneOf(tok::comma, tok::r_square, tok::kw_alignas, - tok::l_square)) && + tok::identifier, tok::l_square, tok::ellipsis)) && !(Tok.is(tok::r_square) && NextToken().isOneOf(tok::equal, tok::l_brace))) { PA.Revert(); return ParseMisplacedBracketDeclarator(D); } + SourceLocation PrevEllipsisLoc; SmallVector<DecompositionDeclarator::Binding, 32> Bindings; while (Tok.isNot(tok::r_square)) { if (!Bindings.empty()) { @@ -7331,11 +7332,11 @@ void Parser::ParseDecompositionDeclarator(Declarator &D) { Diag(Tok, diag::err_expected_comma_or_rsquare); } - SkipUntil(tok::r_square, tok::comma, tok::identifier, + SkipUntil({tok::r_square, tok::comma, tok::identifier, tok::ellipsis}, StopAtSemi | StopBeforeMatch); if (Tok.is(tok::comma)) ConsumeToken(); - else if (Tok.isNot(tok::identifier)) + else if (Tok.is(tok::r_square)) break; } } @@ -7343,6 +7344,21 @@ void Parser::ParseDecompositionDeclarator(Declarator &D) { if (isCXX11AttributeSpecifier()) DiagnoseAndSkipCXX11Attributes(); + SourceLocation EllipsisLoc; + + if (Tok.is(tok::ellipsis)) { + Diag(Tok, getLangOpts().CPlusPlus26 ? diag::warn_cxx23_compat_binding_pack + : diag::ext_cxx_binding_pack); + if (PrevEllipsisLoc.isValid()) { + Diag(Tok, diag::err_binding_multiple_ellipses); + Diag(PrevEllipsisLoc, diag::note_previous_ellipsis); + break; + } + EllipsisLoc = Tok.getLocation(); + PrevEllipsisLoc = EllipsisLoc; + ConsumeToken(); + } + if (Tok.isNot(tok::identifier)) { Diag(Tok, diag::err_expected) << tok::identifier; break; @@ -7352,6 +7368,13 @@ void Parser::ParseDecompositionDeclarator(Declarator &D) { SourceLocation Loc = Tok.getLocation(); ConsumeToken(); + if (Tok.is(tok::ellipsis) && !PrevEllipsisLoc.isValid()) { + DiagnoseMisplacedEllipsis(Tok.getLocation(), Loc, EllipsisLoc.isValid(), + true); + EllipsisLoc = Tok.getLocation(); + ConsumeToken(); + } + ParsedAttributes Attrs(AttrFactory); if (isCXX11AttributeSpecifier()) { Diag(Tok, getLangOpts().CPlusPlus26 @@ -7360,7 +7383,7 @@ void Parser::ParseDecompositionDeclarator(Declarator &D) { MaybeParseCXX11Attributes(Attrs); } - Bindings.push_back({II, Loc, std::move(Attrs)}); + Bindings.push_back({II, Loc, std::move(Attrs), EllipsisLoc}); } if (Tok.isNot(tok::r_square)) diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index c5a72cf812ebc..aced10b21cea0 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -888,7 +888,15 @@ Sema::ActOnDecompositionDeclarator(Scope *S, Declarator &D, Previous.clear(); } - auto *BD = BindingDecl::Create(Context, DC, B.NameLoc, VarName); + QualType QT; + if (B.EllipsisLoc.isValid()) { + if (!cast<Decl>(DC)->isTemplated()) + Diag(B.EllipsisLoc, diag::err_pack_outside_template); + QT = Context.getPackExpansionType(Context.DependentTy, std::nullopt, + /*ExpectsPackInType=*/false); + } + + auto *BD = BindingDecl::Create(Context, DC, B.NameLoc, B.Name, QT); ProcessDeclAttributeList(S, BD, *B.Attrs); @@ -951,20 +959,68 @@ Sema::ActOnDecompositionDeclarator(Scope *S, Declarator &D, return New; } +// Check the arity of the structured bindings. +// Create the resolved pack expr if needed. +static bool CheckBindingsCount(Sema &S, DecompositionDecl *DD, + QualType DecompType, + ArrayRef<BindingDecl *> Bindings, + unsigned MemberCount) { + auto BindingWithPackItr = + std::find_if(Bindings.begin(), Bindings.end(), + [](BindingDecl *D) -> bool { return D->isParameterPack(); }); + bool HasPack = BindingWithPackItr != Bindings.end(); + bool IsValid; + if (!HasPack) { + IsValid = Bindings.size() == MemberCount; + } else { + // There may not be more members than non-pack bindings. + IsValid = MemberCount >= Bindings.size() - 1; + } + + if (IsValid && HasPack) { + // Create the pack expr and assign it to the binding. + unsigned PackSize = MemberCount - Bindings.size() + 1; + QualType PackType = S.Context.getPackExpansionType( + S.Context.DependentTy, std::nullopt, /*ExpectsPackInType=*/false); + BindingDecl *BD = (*BindingWithPackItr); + auto *RP = ResolvedUnexpandedPackExpr::Create(S.Context, DD->getBeginLoc(), + DecompType, PackSize); + BD->setDecomposedDecl(DD); + BD->setBinding(PackType, RP); + + BindingDecl *BPack = *BindingWithPackItr; + // Create the nested BindingDecls. + for (Expr *&E : RP->getExprs()) { + auto *NestedBD = BindingDecl::Create(S.Context, BPack->getDeclContext(), + BPack->getLocation(), + BPack->getIdentifier(), QualType()); + NestedBD->setDecomposedDecl(DD); + E = S.BuildDeclRefExpr(NestedBD, S.Context.DependentTy, VK_LValue, + BPack->getLocation()); + } + } + + if (IsValid) + return false; + + S.Diag(DD->getLocation(), diag::err_decomp_decl_wrong_number_bindings) + << DecompType << (unsigned)Bindings.size() << MemberCount << MemberCount + << (MemberCount < Bindings.size()); + return true; +} + static bool checkSimpleDecomposition( Sema &S, ArrayRef<BindingDecl *> Bindings, ValueDecl *Src, - QualType DecompType, const llvm::APSInt &NumElems, QualType ElemType, + QualType DecompType, const llvm::APSInt &NumElemsAPS, QualType ElemType, llvm::function_ref<ExprResult(SourceLocation, Expr *, unsigned)> GetInit) { - if ((int64_t)Bindings.size() != NumElems) { - S.Diag(Src->getLocation(), diag::err_decomp_decl_wrong_number_bindings) - << DecompType << (unsigned)Bindings.size() - << (unsigned)NumElems.getLimitedValue(UINT_MAX) - << toString(NumElems, 10) << (NumElems < Bindings.size()); + unsigned NumElems = (unsigned)NumElemsAPS.getLimitedValue(UINT_MAX); + auto *DD = cast<DecompositionDecl>(Src); + + if (CheckBindingsCount(S, DD, DecompType, Bindings, NumElems)) return true; - } unsigned I = 0; - for (auto *B : Bindings) { + for (auto *B : DD->flat_bindings()) { SourceLocation Loc = B->getLocation(); ExprResult E = S.BuildDeclRefExpr(Src, DecompType, VK_LValue, Loc); if (E.isInvalid()) @@ -1210,13 +1266,10 @@ static bool checkTupleLikeDecomposition(Sema &S, ArrayRef<BindingDecl *> Bindings, VarDecl *Src, QualType DecompType, const llvm::APSInt &TupleSize) { - if ((int64_t)Bindings.size() != TupleSize) { - S.Diag(Src->getLocation(), diag::err_decomp_decl_wrong_number_bindings) - << DecompType << (unsigned)Bindings.size() - << (unsigned)TupleSize.getLimitedValue(UINT_MAX) - << toString(TupleSize, 10) << (TupleSize < Bindings.size()); + auto *DD = cast<DecompositionDecl>(Src); + unsigned NumElems = (unsigned)TupleSize.getLimitedValue(UINT_MAX); + if (CheckBindingsCount(S, DD, DecompType, Bindings, NumElems)) return true; - } if (Bindings.empty()) return false; @@ -1250,7 +1303,7 @@ static bool checkTupleLikeDecomposition(Sema &S, } unsigned I = 0; - for (auto *B : Bindings) { + for (auto *B : DD->flat_bindings()) { InitializingBinding InitContext(S, B); SourceLocation Loc = B->getLocation(); @@ -1433,20 +1486,18 @@ static bool checkMemberDecomposition(Sema &S, ArrayRef<BindingDecl*> Bindings, QualType BaseType = S.Context.getQualifiedType(S.Context.getRecordType(RD), DecompType.getQualifiers()); - auto DiagnoseBadNumberOfBindings = [&]() -> bool { - unsigned NumFields = llvm::count_if( - RD->fields(), [](FieldDecl *FD) { return !FD->isUnnamedBitField(); }); - assert(Bindings.size() != NumFields); - S.Diag(Src->getLocation(), diag::err_decomp_decl_wrong_number_bindings) - << DecompType << (unsigned)Bindings.size() << NumFields << NumFields - << (NumFields < Bindings.size()); + auto *DD = cast<DecompositionDecl>(Src); + unsigned NumFields = llvm::count_if( + RD->fields(), [](FieldDecl *FD) { return !FD->isUnnamedBitField(); }); + if (CheckBindingsCount(S, DD, DecompType, Bindings, NumFields)) return true; - }; // all of E's non-static data members shall be [...] well-formed // when named as e.name in the context of the structured binding, // E shall not have an anonymous union member, ... - unsigned I = 0; + auto FlatBindings = DD->flat_bindings(); + assert(llvm::range_size(FlatBindings) == NumFields); + auto FlatBindingsItr = FlatBindings.begin(); for (auto *FD : RD->fields()) { if (FD->isUnnamedBitField()) continue; @@ -1471,9 +1522,8 @@ static bool checkMemberDecomposition(Sema &S, ArrayRef<BindingDecl*> Bindings, } // We have a real field to bind. - if (I >= Bindings.size()) - return DiagnoseBadNumberOfBindings(); - auto *B = Bindings[I++]; + assert(FlatBindingsItr != FlatBindings.end()); + BindingDecl *B = *(FlatBindingsItr++); SourceLocation Loc = B->getLocation(); // The field must be accessible in the context of the structured binding. @@ -1511,9 +1561,6 @@ static bool checkMemberDecomposition(Sema &S, ArrayRef<BindingDecl*> Bindings, B->setBinding(S.BuildQualifiedType(FD->getType(), Loc, Q), E.get()); } - if (I != Bindings.size()) - return DiagnoseBadNumberOfBindings(); - return false; } @@ -1523,8 +1570,12 @@ void Sema::CheckCompleteDecompositionDeclaration(DecompositionDecl *DD) { // If the type of the decomposition is dependent, then so is the type of // each binding. if (DecompType->isDependentType()) { - for (auto *B : DD->bindings()) - B->setType(Context.DependentTy); + // Note that all of the types are still Null or PackExpansionType. + for (auto *B : DD->bindings()) { + // Do not overwrite any pack type. + if (B->getType().isNull()) + B->setType(Context.DependentTy); + } return; } diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp index ac3666394d0e8..4cd277e382163 100644 --- a/clang/lib/Sema/SemaExceptionSpec.cpp +++ b/clang/lib/Sema/SemaExceptionSpec.cpp @@ -1068,7 +1068,7 @@ static CanThrowResult canVarDeclThrow(Sema &Self, const VarDecl *VD) { // If this is a decomposition declaration, bindings might throw. if (auto *DD = dyn_cast<DecompositionDecl>(VD)) - for (auto *B : DD->bindings()) + for (auto *B : DD->flat_bindings()) if (auto *HD = B->getHoldingVar()) CT = mergeCanThrow(CT, canVarDeclThrow(Self, HD)); @@ -1278,6 +1278,7 @@ CanThrowResult Sema::canThrow(const Stmt *S) { case Expr::ConvertVectorExprClass: case Expr::VAArgExprClass: case Expr::CXXParenListInitExprClass: + case Expr::ResolvedUnexpandedPackExprClass: return canSubStmtsThrow(*this, S); case Expr::CompoundLiteralExprClass: diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index d9149f7ee40bb..c0499997db37e 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -2670,8 +2670,10 @@ StmtResult Sema::BuildCXXForRangeStmt( // them in properly when we instantiate the loop. if (!LoopVar->isInvalidDecl() && Kind != BFRK_Check) { if (auto *DD = dyn_cast<DecompositionDecl>(LoopVar)) - for (auto *Binding : DD->bindings()) - Binding->setType(Context.DependentTy); + for (auto *Binding : DD->bindings()) { + if (!Binding->isParameterPack()) + Binding->setType(Context.DependentTy); + } LoopVar->setType(SubstAutoTypeDependent(LoopVar->getType())); } } else if (!BeginDeclStmt.get()) { diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index fb0f38df62a74..a28b5979bc1d9 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -1569,6 +1569,10 @@ namespace { /// pack. ExprResult TransformFunctionParmPackExpr(FunctionParmPackExpr *E); + // Transform a ResolvedUnexpandedPackExpr + ExprResult + TransformResolvedUnexpandedPackExpr(ResolvedUnexpandedPackExpr *E); + QualType TransformFunctionProtoType(TypeLocBuilder &TLB, FunctionProtoTypeLoc TL) { // Call the base version; it will forward to our overridden version below. @@ -1851,7 +1855,8 @@ bool TemplateInstantiator::AlreadyTransformed(QualType T) { if (T.isNull()) return true; - if (T->isInstantiationDependentType() || T->isVariablyModifiedType()) + if (T->isInstantiationDependentType() || T->isVariablyModifiedType() || + T->containsUnexpandedParameterPack()) return false; getSema().MarkDeclarationsReferencedInType(Loc, T); @@ -2484,6 +2489,15 @@ TemplateInstantiator::TransformDeclRefExpr(DeclRefExpr *E) { if (PD->isParameterPack()) return TransformFunctionParmPackRefExpr(E, PD); + if (BindingDecl *BD = dyn_cast<BindingDecl>(D); BD && BD->isParameterPack()) { + BD = cast_or_null<BindingDecl>(TransformDecl(BD->getLocation(), BD)); + if (!BD) + return ExprError(); + if (auto *RP = + dyn_cast_if_present<ResolvedUnexpandedPackExpr>(BD->getBinding())) + return TransformResolvedUnexpandedPackExpr(RP); + } + return inherited::TransformDeclRefExpr(E); } @@ -2648,6 +2662,19 @@ TemplateInstantiator::TransformTemplateTypeParmType(TypeLocBuilder &TLB, return Result; } +ExprResult TemplateInstantiator::TransformResolvedUnexpandedPackExpr( + ResolvedUnexpandedPackExpr *E) { + if (getSema().ArgumentPackSubstitutionIndex != -1) { + assert(static_cast<unsigned>(getSema().ArgumentPackSubstitutionIndex) < + E->getNumExprs() && + "ArgumentPackSubstitutionIndex is out of range"); + return TransformExpr( + E->getExpansion(getSema().ArgumentPackSubstitutionIndex)); + } + + return inherited::TransformResolvedUnexpandedPackExpr(E); +} + QualType TemplateInstantiator::TransformSubstTemplateTypeParmPackType( TypeLocBuilder &TLB, SubstTemplateTypeParmPackTypeLoc TL, bool SuppressObjCLifetime) { diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index e058afe81da58..686912ef7b694 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -1166,26 +1166,57 @@ TemplateDeclInstantiator::VisitTypeAliasTemplateDecl(TypeAliasTemplateDecl *D) { Decl *TemplateDeclInstantiator::VisitBindingDecl(BindingDecl *D) { auto *NewBD = BindingDecl::Create(SemaRef.Context, Owner, D->getLocation(), - D->getIdentifier()); + D->getIdentifier(), D->getType()); NewBD->setReferenced(D->isReferenced()); SemaRef.CurrentInstantiationScope->InstantiatedLocal(D, NewBD); + return NewBD; } Decl *TemplateDeclInstantiator::VisitDecompositionDecl(DecompositionDecl *D) { // Transform the bindings first. + // The transformed DD will have all of the concrete BindingDecls. SmallVector<BindingDecl*, 16> NewBindings; - for (auto *OldBD : D->bindings()) + ResolvedUnexpandedPackExpr *OldResolvedPack = nullptr; + for (auto *OldBD : D->bindings()) { + Expr *BindingExpr = OldBD->getBinding(); + if (auto *RP = + dyn_cast_if_present<ResolvedUnexpandedPackExpr>(BindingExpr)) { + assert(!OldResolvedPack && "no more than one pack is allowed"); + OldResolvedPack = RP; + } NewBindings.push_back(cast<BindingDecl>(VisitBindingDecl(OldBD))); + } ArrayRef<BindingDecl*> NewBindingArray = NewBindings; - auto *NewDD = cast_or_null<DecompositionDecl>( + auto *NewDD = cast_if_present<DecompositionDecl>( VisitVarDecl(D, /*InstantiatingVarTemplate=*/false, &NewBindingArray)); if (!NewDD || NewDD->isInvalidDecl()) for (auto *NewBD : NewBindings) NewBD->setInvalidDecl(); + if (OldResolvedPack) { + // Mark the holding vars (if any) in the pack as instantiated since + // they are created implicitly. + auto Bindings = NewDD->bindings(); + auto BPack = llvm::find_if( + Bindings, [](BindingDecl *D) -> bool { return D->isParameterPack(); }); + auto *NewResolvedPack = + cast<ResolvedUnexpandedPackExpr>((*BPack)->getBinding()); + auto OldExprs = OldResolvedPack->getExprs(); + auto NewExprs = NewResolvedPack->getExprs(); + assert(OldExprs.size() == NewExprs.size()); + for (unsigned I = 0; I < OldResolvedPack->getNumExprs(); I++) { + DeclRefExpr *OldDRE = cast<DeclRefExpr>(OldExprs[I]); + BindingDecl *OldNestedBD = cast<BindingDecl>(OldDRE->getDecl()); + DeclRefExpr *NewDRE = cast<DeclRefExpr>(NewExprs[I]); + BindingDecl *NewNestedBD = cast<BindingDecl>(NewDRE->getDecl()); + SemaRef.CurrentInstantiationScope->InstantiatedLocal(OldNestedBD, + NewNestedBD); + } + } + return NewDD; } @@ -6201,8 +6232,16 @@ NamedDecl *Sema::FindInstantiatedDecl(SourceLocation Loc, NamedDecl *D, // declarations to their instantiations. if (CurrentInstantiationScope) { if (auto Found = CurrentInstantiationScope->findInstantiationOf(D)) { - if (Decl *FD = Found->dyn_cast<Decl *>()) + if (Decl *FD = Found->dyn_cast<Decl *>()) { + if (auto *BD = dyn_cast<BindingDecl>(FD); + BD && BD->isParameterPack() && + ArgumentPackSubstitutionIndex != -1) { + auto *DRE = cast<DeclRefExpr>( + BD->getBindingPackExprs()[ArgumentPackSubstitutionIndex]); + return cast<NamedDecl>(DRE->getDecl()); + } return cast<NamedDecl>(FD); + } int PackIdx = ArgumentPackSubstitutionIndex; assert(PackIdx != -1 && diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp index c8452db6bc901..3c56794722dcc 100644 --- a/clang/lib/Sema/SemaTemplateVariadic.cpp +++ b/clang/lib/Sema/SemaTemplateVariadic.cpp @@ -50,17 +50,29 @@ class CollectUnexpandedParameterPacksVisitor auto *FTD = FD ? FD->getDescribedFunctionTemplate() : nullptr; if (FTD && FTD->getTemplateParameters()->getDepth() >= DepthLimit) return; - } else if (getDepthAndIndex(ND).first >= DepthLimit) + } else if (auto *BD = dyn_cast<BindingDecl>(ND)) { + Expr *E = BD->getBinding(); + if (auto *RP = cast_if_present<ResolvedUnexpandedPackExpr>(E)) { + addUnexpanded(RP); + return; + } + } else if (getDepthAndIndex(ND).first >= DepthLimit) { return; + } Unexpanded.push_back({ND, Loc}); } + void addUnexpanded(const TemplateTypeParmType *T, SourceLocation Loc = SourceLocation()) { if (T->getDepth() < DepthLimit) Unexpanded.push_back({T, Loc}); } + void addUnexpanded(ResolvedUnexpandedPackExpr *E) { + Unexpanded.push_back({E, E->getBeginLoc()}); + } + public: explicit CollectUnexpandedParameterPacksVisitor( SmallVectorImpl<UnexpandedParameterPack> &Unexpanded) @@ -103,6 +115,12 @@ class CollectUnexpandedParameterPacksVisitor return true; } + bool + VisitResolvedUnexpandedPackExpr(ResolvedUnexpandedPackExpr *E) override { + addUnexpanded(E); + return true; + } + /// Record occurrences of template template parameter packs. bool TraverseTemplateName(TemplateName Template) override { if (auto *TTP = dyn_cast_or_null<TemplateTemplateParmDecl>( @@ -422,8 +440,8 @@ Sema::DiagnoseUnexpandedParameterPacks(SourceLocation Loc, if (const TemplateTypeParmType *TTP = Unexpanded[I].first.dyn_cast<const TemplateTypeParmType *>()) Name = TTP->getIdentifier(); - else - Name = cast<NamedDecl *>(Unexpanded[I].first)->getIdentifier(); + else if (NamedDecl *ND = Unexpanded[I].first.dyn_cast<NamedDecl *>()) + Name = ND->getIdentifier(); if (Name && NamesKnown.insert(Name).second) Names.push_back(Name); @@ -757,23 +775,39 @@ bool Sema::CheckParameterPacksForExpansion( bool HaveFirstPack = false; std::optional<unsigned> NumPartialExpansions; SourceLocation PartiallySubstitutedPackLoc; + typedef LocalInstantiationScope::DeclArgumentPack DeclArgumentPack; for (UnexpandedParameterPack ParmPack : Unexpanded) { // Compute the depth and index for this parameter pack. unsigned Depth = 0, Index = 0; IdentifierInfo *Name; bool IsVarDeclPack = false; + ResolvedUnexpandedPackExpr *ResolvedPack = nullptr; if (const TemplateTypeParmType *TTP = ParmPack.first.dyn_cast<const TemplateTypeParmType *>()) { Depth = TTP->getDepth(); Index = TTP->getIndex(); Name = TTP->getIdentifier(); + } else if (auto *RP = + ParmPack.first.dyn_cast<ResolvedUnexpandedPackExpr *>()) { + ResolvedPack = RP; } else { NamedDecl *ND = cast<NamedDecl *>(ParmPack.first); if (isa<VarDecl>(ND)) IsVarDeclPack = true; - else + else if (isa<BindingDecl>(ND)) { + // Find the instantiated BindingDecl and check it for a resolved pack. + llvm::PointerUnion<Decl *, DeclArgumentPack *> *Instantiation = + CurrentInstantiationScope->findInstantiationOf(ND); + Decl *B = cast<Decl *>(*Instantiation); + Expr *BindingExpr = cast<BindingDecl>(B)->getBinding(); + ResolvedPack = cast_if_present<ResolvedUnexpandedPackExpr>(BindingExpr); + if (!ResolvedPack) { + ShouldExpand = false; + continue; + } + } else std::tie(Depth, Index) = getDepthAndIndex(ND); Name = ND->getIdentifier(); @@ -783,8 +817,6 @@ bool Sema::CheckParameterPacksForExpansion( unsigned NewPackSize, PendingPackExpansionSize = 0; if (IsVarDeclPack) { // Figure out whether we're instantiating to an argument pack or not. - typedef LocalInstantiationScope::DeclArgumentPack DeclArgumentPack; - llvm::PointerUnion<Decl *, DeclArgumentPack *> *Instantiation = CurrentInstantiationScope->findInstantiationOf( cast<NamedDecl *>(ParmPack.first)); @@ -797,6 +829,8 @@ bool Sema::CheckParameterPacksForExpansion( ShouldExpand = false; continue; } + } else if (ResolvedPack) { + NewPackSize = ResolvedPack->getNumExprs(); } else { // If we don't have a template argument at this depth/index, then we // cannot expand the pack expansion. Make a note of this, but we still @@ -833,7 +867,7 @@ bool Sema::CheckParameterPacksForExpansion( // Template argument deduction can extend the sequence of template // arguments corresponding to a template parameter pack, even when the // sequence contains explicitly specified template arguments. - if (!IsVarDeclPack && CurrentInstantiationScope) { + if (!IsVarDeclPack && !ResolvedPack && CurrentInstantiationScope) { if (NamedDecl *PartialPack = CurrentInstantiationScope->getPartiallySubstitutedPack()) { unsigned PartialDepth, PartialIndex; @@ -939,6 +973,12 @@ std::optional<unsigned> Sema::getNumArgumentsInExpansionFromUnexpanded( Unexpanded[I].first.dyn_cast<const TemplateTypeParmType *>()) { Depth = TTP->getDepth(); Index = TTP->getIndex(); + } else if (auto *PE = Unexpanded[I] + .first.dyn_cast<ResolvedUnexpandedPackExpr *>()) { + unsigned Size = PE->getNumExprs(); + assert((!Result || *Result == Size) && "inconsistent pack sizes"); + Result = Size; + continue; } else { NamedDecl *ND = cast<NamedDecl *>(Unexpanded[I].first); if (isa<VarDecl>(ND)) { @@ -1167,8 +1207,12 @@ ExprResult Sema::ActOnSizeofParameterPackExpr(Scope *S, MarkAnyDeclReferenced(OpLoc, ParameterPack, true); + std::optional<unsigned> Length; + if (auto *RP = ResolvedUnexpandedPackExpr::getFromDecl(ParameterPack)) + Length = RP->getNumExprs(); + return SizeOfPackExpr::Create(Context, OpLoc, ParameterPack, NameLoc, - RParenLoc); + RParenLoc, Length); } static bool isParameterPack(Expr *PackExpression) { diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 5d43d98ce49e4..f59b4fdb5e60a 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -3676,6 +3676,13 @@ class TreeTransform { FullySubstituted); } + ExprResult RebuildResolvedUnexpandedPackExpr(SourceLocation BeginLoc, + QualType T, + ArrayRef<Expr *> Exprs) { + return ResolvedUnexpandedPackExpr::Create(SemaRef.Context, BeginLoc, T, + Exprs); + } + /// Build a new expression representing a call to a source location /// builtin. /// @@ -15291,12 +15298,11 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) { // The transform has determined that we should perform an expansion; // transform and capture each of the arguments. // expansion of the pattern. Do so. - auto *Pack = cast<VarDecl>(C->getCapturedVar()); + auto *Pack = cast<ValueDecl>(C->getCapturedVar()); for (unsigned I = 0; I != *NumExpansions; ++I) { Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(getSema(), I); - VarDecl *CapturedVar - = cast_or_null<VarDecl>(getDerived().TransformDecl(C->getLocation(), - Pack)); + ValueDecl *CapturedVar = cast_if_present<ValueDecl>( + getDerived().TransformDecl(C->getLocation(), Pack)); if (!CapturedVar) { Invalid = true; continue; @@ -15991,6 +15997,24 @@ TreeTransform<Derived>::TransformFunctionParmPackExpr(FunctionParmPackExpr *E) { return E; } +template <typename Derived> +ExprResult TreeTransform<Derived>::TransformResolvedUnexpandedPackExpr( + ResolvedUnexpandedPackExpr *E) { + bool ArgumentChanged = false; + SmallVector<Expr *, 12> NewExprs; + if (TransformExprs(E->getExprs().begin(), E->getNumExprs(), + /*IsCall=*/false, NewExprs, &ArgumentChanged)) + return ExprError(); + + if (!AlwaysRebuild() && !ArgumentChanged) + return E; + + // NOTE: The type is just a superficial PackExpansionType + // that needs no substitution. + return RebuildResolvedUnexpandedPackExpr(E->getBeginLoc(), E->getType(), + NewExprs); +} + template<typename Derived> ExprResult TreeTransform<Derived>::TransformMaterializeTemporaryExpr( diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp index 9e8cf19a6f0f7..cb26228a37fcd 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -2201,6 +2201,16 @@ void ASTStmtReader::VisitPackIndexingExpr(PackIndexingExpr *E) { Exprs[I] = Record.readExpr(); } +void ASTStmtReader::VisitResolvedUnexpandedPackExpr( + ResolvedUnexpandedPackExpr *E) { + VisitExpr(E); + E->NumExprs = Record.readInt(); + E->BeginLoc = readSourceLocation(); + auto **Exprs = E->getTrailingObjects<Expr *>(); + for (unsigned I = 0; I < E->NumExprs; ++I) + Exprs[I] = Record.readExpr(); +} + void ASTStmtReader::VisitSubstNonTypeTemplateParmExpr( SubstNonTypeTemplateParmExpr *E) { VisitExpr(E); @@ -4270,6 +4280,12 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) { /*TransformedExprs=*/Record[ASTStmtReader::NumExprFields]); break; + case EXPR_RESOLVED_UNEXPANDED_PACK: + S = ResolvedUnexpandedPackExpr::CreateDeserialized( + Context, + /*NumExprs=*/Record[ASTStmtReader::NumExprFields]); + break; + case EXPR_SUBST_NON_TYPE_TEMPLATE_PARM: S = new (Context) SubstNonTypeTemplateParmExpr(Empty); break; diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index 4a6027943072c..2b531659f1920 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -874,6 +874,7 @@ static void AddStmtsExprs(llvm::BitstreamWriter &Stream, RECORD(EXPR_PACK_EXPANSION); RECORD(EXPR_SIZEOF_PACK); RECORD(EXPR_PACK_INDEXING); + RECORD(EXPR_RESOLVED_UNEXPANDED_PACK); RECORD(EXPR_SUBST_NON_TYPE_TEMPLATE_PARM); RECORD(EXPR_SUBST_NON_TYPE_TEMPLATE_PARM_PACK); RECORD(EXPR_FUNCTION_PARM_PACK); diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp index 1d42b43c3e2ca..60470ea2f367a 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -2202,6 +2202,16 @@ void ASTStmtWriter::VisitPackIndexingExpr(PackIndexingExpr *E) { Code = serialization::EXPR_PACK_INDEXING; } +void ASTStmtWriter::VisitResolvedUnexpandedPackExpr( + ResolvedUnexpandedPackExpr *E) { + VisitExpr(E); + Record.push_back(E->getNumExprs()); + Record.AddSourceLocation(E->getBeginLoc()); + for (Expr *Sub : E->getExprs()) + Record.AddStmt(Sub); + Code = serialization::EXPR_RESOLVED_UNEXPANDED_PACK; +} + void ASTStmtWriter::VisitSubstNonTypeTemplateParmExpr( SubstNonTypeTemplateParmExpr *E) { VisitExpr(E); diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index db385e891e762..312c7870948cd 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1743,6 +1743,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::DependentCoawaitExprClass: case Stmt::CoreturnStmtClass: case Stmt::CoyieldExprClass: + case Stmt::ResolvedUnexpandedPackExprClass: case Stmt::SEHTryStmtClass: case Stmt::SEHExceptStmtClass: case Stmt::SEHLeaveStmtClass: diff --git a/clang/test/AST/ast-dump-binding-pack.cpp b/clang/test/AST/ast-dump-binding-pack.cpp new file mode 100644 index 0000000000000..81c75a1268730 --- /dev/null +++ b/clang/test/AST/ast-dump-binding-pack.cpp @@ -0,0 +1,74 @@ +// RUN: %clang_cc1 -ast-dump -std=c++26 %s | FileCheck %s + +// Test this with PCH. +// RUN: %clang_cc1 %s -std=c++26 -emit-pch -o %t %s +// RUN: %clang_cc1 %s -std=c++26 -include-pch %t -ast-dump-all | FileCheck %s + +#ifndef PCH_HELPER +#define PCH_HELPER + +template <unsigned N> +void foo() { + int arr[4] = {1, 2, 3, 4}; + auto [binding_1, ...binding_rest, binding_4] = arr; + int arr_2[] = {binding_rest...}; +}; + +// CHECK-LABEL: FunctionTemplateDecl {{.*}} foo +// CHECK-LABEL: BindingDecl {{.*}} binding_1 +// CHECK-NEXT: ArraySubscriptExpr {{.*}} +// CHECK-NEXT: ImplicitCastExpr +// CHECK-NEXT: DeclRefExpr {{.*}} +// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 0 +// CHECK-NOT: BindingDecl +// CHECK-LABEL: BindingDecl {{.*}} binding_rest +// CHECK-NEXT: ResolvedUnexpandedPackExpr +// CHECK-NEXT: DeclRefExpr {{.*}} lvalue Binding {{.*}} 'binding_rest' +// CHECK-NEXT: DeclRefExpr {{.*}} lvalue Binding {{.*}} 'binding_rest' +// CHECK-NOT: BindingDecl +// CHECK-LABEL: BindingDecl {{.*}} binding_4 +// CHECK-NEXT: ArraySubscriptExpr +// CHECK-NEXT: ImplicitCastExpr {{.*}} +// CHECK-NEXT: DeclRefExpr {{.*}} lvalue Decomposition {{.*}} +// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 3 +// CHECK-NOT: BindingDecl +// CHECK-LABEL: VarDecl {{.*}} arr_2 +// CHECK-NEXT: InitListExpr +// CHECK-NEXT: PackExpansionExpr {{.*}} '<dependent type>' lvalue +// CHECK-NEXT: DeclRefExpr {{.*}} lvalue Binding {{.*}} 'binding_rest' + +struct tag_t { }; +template <unsigned N> +void bar() { + auto [...empty_binding_pack] = tag_t{}; + static_assert(sizeof...(empty_binding_pack) == 0); +}; + +// CHECK-LABEL: FunctionTemplateDecl {{.*}} bar +// CHECK-NOT: BindingDecl +// CHECK-LABEL: BindingDecl {{.*}} empty_binding_pack +// CHECK-NEXT: ResolvedUnexpandedPackExpr +// CHECK-NOT: DeclRefExpr {{.*}} 'empty_binding_pack' +// CHECK-NOT: BindingDecl +// CHECK: DeclStmt + +struct int_pair { int x; int y; }; +template <typename T> +void baz() { + auto [binding_1, binding_2, ...empty_binding_pack] = T{}; + static_assert(sizeof...(empty_binding_pack) == 0); +}; + +void(*f)() = baz<int_pair>; + +// CHECK-LABEL: FunctionDecl {{.*}} baz {{.*}} implicit_instantiation +// CHECK-NEXT: TemplateArgument type 'int_pair' +// CHECK: BindingDecl {{.*}} binding_1 +// CHECK: BindingDecl {{.*}} binding_2 +// CHECK-NOT: BindingDecl +// CHECK-LABEL: BindingDecl {{.*}} empty_binding_pack +// CHECK-NEXT: ResolvedUnexpandedPackExpr +// CHECK-NOT: DeclRefExpr {{.*}} 'empty_binding_pack' +// CHECK-NOT: BindingDecl +// CHECK: DeclStmt +#endif diff --git a/clang/test/CodeGenCXX/cxx2c-decomposition.cpp b/clang/test/CodeGenCXX/cxx2c-decomposition.cpp new file mode 100644 index 0000000000000..27562524fd4b0 --- /dev/null +++ b/clang/test/CodeGenCXX/cxx2c-decomposition.cpp @@ -0,0 +1,56 @@ +// RUN: %clang_cc1 -std=c++26 -triple x86_64-linux-gnu -emit-llvm -o - %s | FileCheck %s + +namespace std { + using size_t = decltype(sizeof(0)); + template<typename> struct tuple_size; + template<size_t, typename> struct tuple_element; +} + +struct Y { int n; }; +struct X { X(); X(Y); X(const X&); ~X(); }; + +struct A { int a : 13; bool b; }; + +struct B {}; +template<> struct std::tuple_size<B> { enum { value = 2 }; }; +template<> struct std::tuple_element<0,B> { using type = X; }; +template<> struct std::tuple_element<1,B> { using type = const int&; }; +template<int N> auto get(B) { + if constexpr (N == 0) + return Y(); + else + return 0.0; +} + +using C = int[2]; + +template<typename T> T &make(); + +// CHECK-LABEL: define {{.*}} @_Z8big_testIiEiv() +template <typename T> +int big_test() { + A& a = make<A>(); +A: + auto &[...an] = a; + an...[0] = 5; + // CHECK: %[[a1:.*]].load = load i16, ptr %[[BITFIELD:.*]], + // CHECK: %[[a1]].clear = and i16 %[[a1]].load, -8192 + // CHECK: %[[a1]].set = or i16 %[[a1]].clear, 5 + // CHECK: store i16 %[[a1]].set, ptr %[[BITFIELD]], +B: + auto [b1, ...bn] = make<B>(); + // CHECK: @_Z4makeI1BERT_v() + // CHECK: call i32 @_Z3getILi0EEDa1B() + // CHECK: call void @_ZN1XC1E1Y(ptr {{[^,]*}} %[[b1:.*]], i32 + // + // CHECK: call noundef double @_Z3getILi1EEDa1B() + // CHECK: %[[cvt:.*]] = fptosi double %{{.*}} to i32 + // CHECK: store i32 %[[cvt]], ptr %[[b2:.*]], + // CHECK: store ptr %[[b2]], ptr %[[b2ref:.*]], + int bn2 = bn...[0]; + // CHECK load ptr, ptr %[[b2ref]] + + return 0; +} + +int g = big_test<int>(); diff --git a/clang/test/Parser/cxx2c-binding-pack.cpp b/clang/test/Parser/cxx2c-binding-pack.cpp new file mode 100644 index 0000000000000..0daaad3a459ed --- /dev/null +++ b/clang/test/Parser/cxx2c-binding-pack.cpp @@ -0,0 +1,25 @@ +// RUN: %clang_cc1 -std=c++2c -verify -fsyntax-only %s + +template <unsigned N> +void decompose_array() { + int arr[4] = {1, 2, 3, 5}; + auto [x, ... // #1 + rest, ...more_rest] = arr; // expected-error{{multiple packs in structured binding declaration}} + // expected-note@#1{{previous binding pack specified here}} + + auto [y...] = arr; // expected-error{{'...' must immediately precede declared identifier}} + + auto [...] = arr; // #2 + // expected-error@#2{{expected identifier}} + // expected-error@#2{{{no names were provided}}} + // expected-warning@#2{{{does not allow a decomposition group to be empty}}} + auto [a, ..., b] = arr; // #3 + // expected-error@#3{{expected identifier}} + // expected-error@#3{{{only 1 name was provided}}} + auto [a1, ...] = arr; // #4 + // expected-error@#4{{expected identifier}} + // expected-error@#4{{{only 1 name was provided}}} + auto [..., b] = arr; // #5 + // expected-error@#5{{expected identifier}} + // expected-error@#5{{{no names were provided}}} +} diff --git a/clang/test/SemaCXX/cxx2c-binding-pack-nontemplate.cpp b/clang/test/SemaCXX/cxx2c-binding-pack-nontemplate.cpp new file mode 100644 index 0000000000000..ea94757dc66b6 --- /dev/null +++ b/clang/test/SemaCXX/cxx2c-binding-pack-nontemplate.cpp @@ -0,0 +1,11 @@ +// RUN: %clang_cc1 -std=c++26 -fsyntax-only %s -verify=nontemplate +// RUN: %clang_cc1 -std=c++2c -verify=cxx26,nontemplate -fsyntax-only -Wpre-c++26-compat %s +// RUN: %clang_cc1 -std=c++23 -verify=cxx23,nontemplate -fsyntax-only -Wc++26-extensions %s + +void decompose_array() { + int arr[4] = {1, 2, 3, 6}; + // cxx26-warning@+3 {{structured binding packs are incompatible with C++ standards before C++2c}} + // cxx23-warning@+2 {{structured binding packs are a C++2c extension}} + // nontemplate-error@+1 {{pack declaration outside of template}} + auto [x, ...rest, y] = arr; +} diff --git a/clang/test/SemaCXX/cxx2c-binding-pack.cpp b/clang/test/SemaCXX/cxx2c-binding-pack.cpp new file mode 100644 index 0000000000000..5ca249f52b3d8 --- /dev/null +++ b/clang/test/SemaCXX/cxx2c-binding-pack.cpp @@ -0,0 +1,190 @@ +// RUN: %clang_cc1 -fsyntax-only -std=c++26 %s -verify + +template <typename T> +struct type_ { }; + +template <typename ...T> +auto sum(T... t) { return (t + ...); } + +struct my_struct { + int a; + int b; + int c; + int d; +}; + +struct fake_tuple { + int arr[4] = {1, 2, 3, 6}; + + template <unsigned i> + int get() { + return arr[i]; + } +}; + +namespace std { + template <typename T> + struct tuple_size; + template <unsigned i, typename T> + struct tuple_element; + + template <> + struct tuple_size<fake_tuple> { + static constexpr unsigned value = 4; + }; + + template <unsigned i> + struct tuple_element<i, fake_tuple> { + using type = int; + }; +} + + +template <typename T> +void decompose_tuple() { + auto tup = T{{1, 2, 3, 6}}; + auto&& [x, ...rest, y] = tup; + + ((void)type_<int>(type_<decltype(rest)>{}), ...); + + T arrtup[2] = {T{{1, 2, 3, 6}}, + T{{7, 9, 10, 11}}}; + int sum = 0; + for (auto [...xs] : arrtup) { + sum += (xs + ...); + } +} + +template <typename T> +void decompose_struct() { + T obj{1, 2, 3, 6}; + auto [x, ...rest, y] = obj; + + auto [...empty] = type_<int>{}; + static_assert(sizeof...(empty) == 0); +} + +template <typename T> +void decompose_array() { + int arr[4] = {1, 2, 3, 6}; + auto [x, ...rest, y] = arr; + + static_assert(sizeof...(rest) == 2); + int size = sizeof...(rest); + T arr2[sizeof...(rest)] = {rest...}; + auto [...pack] = arr2; + + // Array of size 1. + int arr1[1] = {1}; + auto [a, ...b] = arr1; + static_assert(sizeof...(b) == 0); + auto [...c] = arr1; + static_assert(sizeof...(c) == 1); + auto [a1, ...b1, c1] = arr1; // expected-error{{decomposes into 1 element, but 3 names were provided}} +} + +// Test case by Younan Zhang. +template <unsigned... P> +struct S { + template <unsigned... Q> + struct N { + void foo() { + int arr[] = {P..., Q...}; + auto [x, y, ...rest] = arr; + [&]() { + static_assert(sizeof...(rest) + 2 == sizeof...(P) + sizeof...(Q)); + }(); + } + }; +}; + +struct bit_fields { + int a : 4 {1}; + int b : 4 {2}; + int c : 4 {3}; + int d : 4 {4}; +}; + +template <typename T> +void decompose_bit_field() { + auto [...x] = T{}; + static_assert(sizeof...(x) == 4); + int a = x...[0]; + int b = x...[1]; + int c = x...[2]; + int d = x...[3]; +} + +template <typename T> +void lambda_capture() { + auto [...x] = T{}; + [=] { (void)sum(x...); }(); + [&] { (void)sum(x...); }(); + [x...] { (void)sum(x...); }(); + [&x...] { (void)sum(x...); }(); +} + +int main() { + decompose_array<int>(); + decompose_tuple<fake_tuple>(); + decompose_struct<my_struct>(); + S<1, 2, 3, 4>::N<5, 6>().foo(); + decompose_bit_field<bit_fields>(); + lambda_capture<int[5]>(); + lambda_capture<fake_tuple>(); + lambda_capture<my_struct>(); +} + +// P1061R10 Stuff +namespace { +struct C { int x, y, z; }; + +template <class T> +void now_i_know_my() { + auto [a, b, c] = C(); // OK, SB0 is a, SB1 is b, and SB2 is c + auto [d, ...e] = C(); // OK, SB0 is d, the pack e (v1) contains two structured bindings: SB1 and SB2 + static_assert(sizeof...(e) == 2); + auto [...f, g] = C(); // OK, the pack f (v0) contains two structured bindings: SB0 and SB1, and SB2 is g + static_assert(sizeof...(e) == 2); + auto [h, i, j, ...k] = C(); // OK, the pack k is empty + static_assert(sizeof...(e) == 0); + auto [l, m, n, o, ...p] = C(); // expected-error{{{decomposes into 3 elements, but 5 names were provided}}} +} +} // namespace + +namespace { +auto g() -> int(&)[4]; + +template <unsigned long N> +void h(int (&arr)[N]) { + auto [a, ...b, c] = arr; // a names the first element of the array, + // b is a pack referring to the second and + // third elements, and c names the fourth element + static_assert(sizeof...(b) == 2); + auto& [...e] = arr; // e is a pack referring to the four elements of the array + static_assert(sizeof...(e) == 4); +} + +void call_h() { + h(g()); +} +} // namespace + +namespace { +struct D { }; + +int g(...) { return 1; } + +template <typename T> +constexpr int f() { + D arr[1]; + auto [...e] = arr; + return g(e...); +} + +constexpr int g(D) { return 2; } + +void other_main() { + static_assert(f<int>() == 2); +} +} // namespace diff --git a/clang/tools/libclang/CXCursor.cpp b/clang/tools/libclang/CXCursor.cpp index f56e77b42f9d7..694d86a2ce9ae 100644 --- a/clang/tools/libclang/CXCursor.cpp +++ b/clang/tools/libclang/CXCursor.cpp @@ -338,6 +338,7 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent, case Stmt::EmbedExprClass: case Stmt::HLSLOutArgExprClass: case Stmt::OpenACCAsteriskSizeExprClass: + case Stmt::ResolvedUnexpandedPackExprClass: K = CXCursor_UnexposedExpr; break;