[clang] abc8812 - [Clang][P1061] Add stuctured binding packs (#121417)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Jan 29 12:43:56 PST 2025
Author: Jason Rice
Date: 2025-01-29T21:43:52+01:00
New Revision: abc8812df02599fc413d9ed77b992f8236ed2af9
URL: https://github.com/llvm/llvm-project/commit/abc8812df02599fc413d9ed77b992f8236ed2af9
DIFF: https://github.com/llvm/llvm-project/commit/abc8812df02599fc413d9ed77b992f8236ed2af9.diff
LOG: [Clang][P1061] Add stuctured binding packs (#121417)
This is an implementation of P1061 Structure Bindings Introduce a Pack
without the ability to use packs outside of templates. There is a couple
of ways the AST could have been sliced so let me know what you think.
The only part of this change that I am unsure of is the
serialization/deserialization stuff. I followed the implementation of
other Exprs, but I do not really know how it is tested. Thank you for
your time considering this.
---------
Co-authored-by: Yanzuo Liu <zwuis at outlook.com>
Added:
clang/test/AST/ast-dump-binding-pack.cpp
clang/test/CodeGenCXX/cxx2c-decomposition.cpp
clang/test/Parser/cxx2c-binding-pack.cpp
clang/test/SemaCXX/cxx2c-binding-pack-nontemplate.cpp
clang/test/SemaCXX/cxx2c-binding-pack.cpp
Modified:
clang/include/clang/AST/Decl.h
clang/include/clang/AST/DeclCXX.h
clang/include/clang/AST/ExprCXX.h
clang/include/clang/AST/RecursiveASTVisitor.h
clang/include/clang/Basic/DiagnosticParseKinds.td
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/include/clang/Basic/StmtNodes.td
clang/include/clang/Sema/DeclSpec.h
clang/include/clang/Sema/Sema.h
clang/include/clang/Serialization/ASTBitCodes.h
clang/lib/AST/ASTContext.cpp
clang/lib/AST/ASTImporter.cpp
clang/lib/AST/Decl.cpp
clang/lib/AST/DeclBase.cpp
clang/lib/AST/DeclCXX.cpp
clang/lib/AST/Expr.cpp
clang/lib/AST/ExprCXX.cpp
clang/lib/AST/ExprClassification.cpp
clang/lib/AST/ExprConstant.cpp
clang/lib/AST/ItaniumMangle.cpp
clang/lib/AST/StmtPrinter.cpp
clang/lib/AST/StmtProfile.cpp
clang/lib/CodeGen/CGDebugInfo.cpp
clang/lib/CodeGen/CGDecl.cpp
clang/lib/CodeGen/CodeGenModule.cpp
clang/lib/Parse/ParseDecl.cpp
clang/lib/Sema/SemaDeclCXX.cpp
clang/lib/Sema/SemaExceptionSpec.cpp
clang/lib/Sema/SemaStmt.cpp
clang/lib/Sema/SemaTemplateInstantiate.cpp
clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
clang/lib/Sema/SemaTemplateVariadic.cpp
clang/lib/Sema/TreeTransform.h
clang/lib/Serialization/ASTReaderStmt.cpp
clang/lib/Serialization/ASTWriter.cpp
clang/lib/Serialization/ASTWriterStmt.cpp
clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
clang/tools/libclang/CXCursor.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index 362a2741a0cddd..f305cbbce4c609 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 79fd403c2718c8..766821b4fb25cb 100644
--- a/clang/include/clang/AST/DeclCXX.h
+++ b/clang/include/clang/AST/DeclCXX.h
@@ -4175,8 +4175,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;
@@ -4184,7 +4185,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
@@ -4192,14 +4194,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).
@@ -4211,6 +4212,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; }
};
@@ -4238,8 +4243,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;
@@ -4257,8 +4270,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 7b0450b90d5644..0b6c8cfb163c95 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -5319,6 +5319,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 c4a1d03f1b3d10..5f4c39b9cbdb75 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -2950,6 +2950,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 3309f59a981fc1..c513dab810d1f5 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 db911ed121e951..2ac3879a4caabc 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -5945,6 +5945,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 53fc77bbbcecc1..2fea05e322c754 100644
--- a/clang/include/clang/Basic/StmtNodes.td
+++ b/clang/include/clang/Basic/StmtNodes.td
@@ -163,6 +163,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 06243f2624876f..5f5df3a45d41dc 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 6b4959f972cfe5..472a0e25adc975 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -232,7 +232,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;
@@ -6021,6 +6022,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 1b56ed2c9776b5..7656add0b6884c 100644
--- a/clang/include/clang/Serialization/ASTBitCodes.h
+++ b/clang/include/clang/Serialization/ASTBitCodes.h
@@ -1908,6 +1908,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 cd1bcb3b9a063d..4e387da6dccd6f 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -12841,11 +12841,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 09fa10f716ec1f..c9f2f905d2134c 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -2553,7 +2553,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 ba77c748815d56..610207cf8b9a4a 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);
@@ -5421,6 +5417,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 8506b95f761fe5..c0a331d18cab8d 100644
--- a/clang/lib/AST/DeclBase.cpp
+++ b/clang/lib/AST/DeclBase.cpp
@@ -245,7 +245,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 a023a9f456a0e4..e394e0515e5993 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -3462,19 +3462,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 {
@@ -3490,6 +3492,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 06b04914426737..4fc62919fde94b 100644
--- a/clang/lib/AST/Expr.cpp
+++ b/clang/lib/AST/Expr.cpp
@@ -3659,6 +3659,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 5bf5d6adf525a8..d900af895b42a6 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 3f37d06cc8f3a0..5225c3ca773ad4 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 0e41e3dbc8a32a..37019b5235f561 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -5225,7 +5225,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);
@@ -17253,6 +17253,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 49089c0ea3c8ac..e889b74a5cdae3 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -4933,7 +4933,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 9efc88436f928d..84d4e1f8ae3f85 100644
--- a/clang/lib/AST/StmtPrinter.cpp
+++ b/clang/lib/AST/StmtPrinter.cpp
@@ -45,9 +45,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"
@@ -2587,6 +2589,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 5d1f370cac19f3..84985fcb20ff9e 100644
--- a/clang/lib/AST/StmtProfile.cpp
+++ b/clang/lib/AST/StmtProfile.cpp
@@ -2280,6 +2280,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 f0abfaa7324fcd..db595796c067e9 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -5083,10 +5083,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 747e420d031b6f..cc6815db4d20f6 100644
--- a/clang/lib/CodeGen/CGDecl.cpp
+++ b/clang/lib/CodeGen/CGDecl.cpp
@@ -164,9 +164,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 eb8d3ceeeba4c0..a015d64f4065aa 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -7014,9 +7014,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 f136d5007e8a5f..75b5e11f8327ca 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -7315,15 +7315,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()) {
@@ -7338,11 +7339,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;
}
}
@@ -7350,6 +7351,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;
@@ -7359,6 +7375,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
@@ -7367,7 +7390,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 e4e3bbad1f5205..0cf02fe6407c24 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 470d0d753b5580..7b08a066d3cc1b 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));
@@ -1286,6 +1286,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 947651d514b3b0..ec38674a2c3e79 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -2716,8 +2716,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 b4fa23ddf049d5..12e98a33d07853 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -1583,6 +1583,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.
@@ -1848,7 +1852,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);
@@ -2473,6 +2478,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);
}
@@ -2637,6 +2651,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 f540431a73445f..d530ed0847ae86 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -1168,26 +1168,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;
}
@@ -6240,8 +6271,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 c8452db6bc9014..3c56794722dcc7 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 2a5e354ff716a5..808b56448e1ea6 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -3680,6 +3680,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.
///
@@ -15427,12 +15434,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;
@@ -16127,6 +16133,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 990235a310d902..b15eca87993a5b 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -2208,6 +2208,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);
@@ -4291,6 +4301,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 2d0fae8b64d074..e81a441d753b5d 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 2daf8393b1819a..e6701c586e022e 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -2210,6 +2210,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 140c77790496d9..2b1872f8386aad 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 00000000000000..81c75a1268730f
--- /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 00000000000000..27562524fd4b04
--- /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 00000000000000..0daaad3a459ed6
--- /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 00000000000000..ea94757dc66b67
--- /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 at +3 {{structured binding packs are incompatible with C++ standards before C++2c}}
+ // cxx23-warning at +2 {{structured binding packs are a C++2c extension}}
+ // nontemplate-error at +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 00000000000000..5ca249f52b3d8e
--- /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 b9f0b089e41b00..60c740311e940b 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;
More information about the cfe-commits
mailing list