Skip to content

Commit abc8812

Browse files
ricejasonfzwuis
andauthored
[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 <[email protected]>
1 parent 608012a commit abc8812

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+883
-91
lines changed

clang/include/clang/AST/Decl.h

+4-4
Original file line numberDiff line numberDiff line change
@@ -698,6 +698,10 @@ class ValueDecl : public NamedDecl {
698698
return const_cast<ValueDecl *>(this)->getPotentiallyDecomposedVarDecl();
699699
}
700700

701+
/// Determine whether this value is actually a function parameter pack,
702+
/// init-capture pack, or structured binding pack
703+
bool isParameterPack() const;
704+
701705
// Implement isa/cast/dyncast/etc.
702706
static bool classof(const Decl *D) { return classofKind(D->getKind()); }
703707
static bool classofKind(Kind K) { return K >= firstValue && K <= lastValue; }
@@ -1527,10 +1531,6 @@ class VarDecl : public DeclaratorDecl, public Redeclarable<VarDecl> {
15271531
NonParmVarDeclBits.IsInitCapture = IC;
15281532
}
15291533

1530-
/// Determine whether this variable is actually a function parameter pack or
1531-
/// init-capture pack.
1532-
bool isParameterPack() const;
1533-
15341534
/// Whether this local extern variable declaration's previous declaration
15351535
/// was declared in the same block scope. Only correct in C++.
15361536
bool isPreviousDeclInSameBlockScope() const {

clang/include/clang/AST/DeclCXX.h

+48-10
Original file line numberDiff line numberDiff line change
@@ -4175,31 +4175,32 @@ class BindingDecl : public ValueDecl {
41754175
/// binding).
41764176
Expr *Binding = nullptr;
41774177

4178-
BindingDecl(DeclContext *DC, SourceLocation IdLoc, IdentifierInfo *Id)
4179-
: ValueDecl(Decl::Binding, DC, IdLoc, Id, QualType()) {}
4178+
BindingDecl(DeclContext *DC, SourceLocation IdLoc, IdentifierInfo *Id,
4179+
QualType T)
4180+
: ValueDecl(Decl::Binding, DC, IdLoc, Id, T) {}
41804181

41814182
void anchor() override;
41824183

41834184
public:
41844185
friend class ASTDeclReader;
41854186

41864187
static BindingDecl *Create(ASTContext &C, DeclContext *DC,
4187-
SourceLocation IdLoc, IdentifierInfo *Id);
4188+
SourceLocation IdLoc, IdentifierInfo *Id,
4189+
QualType T);
41884190
static BindingDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID);
41894191

41904192
/// Get the expression to which this declaration is bound. This may be null
41914193
/// in two different cases: while parsing the initializer for the
41924194
/// decomposition declaration, and when the initializer is type-dependent.
41934195
Expr *getBinding() const { return Binding; }
41944196

4197+
// Get the array of Exprs when the binding represents a pack.
4198+
llvm::ArrayRef<Expr *> getBindingPackExprs() const;
4199+
41954200
/// Get the decomposition declaration that this binding represents a
41964201
/// decomposition of.
41974202
ValueDecl *getDecomposedDecl() const { return Decomp; }
41984203

4199-
/// Get the variable (if any) that holds the value of evaluating the binding.
4200-
/// Only present for user-defined bindings for tuple-like types.
4201-
VarDecl *getHoldingVar() const;
4202-
42034204
/// Set the binding for this BindingDecl, along with its declared type (which
42044205
/// should be a possibly-cv-qualified form of the type of the binding, or a
42054206
/// reference to such a type).
@@ -4211,6 +4212,10 @@ class BindingDecl : public ValueDecl {
42114212
/// Set the decomposed variable for this BindingDecl.
42124213
void setDecomposedDecl(ValueDecl *Decomposed) { Decomp = Decomposed; }
42134214

4215+
/// Get the variable (if any) that holds the value of evaluating the binding.
4216+
/// Only present for user-defined bindings for tuple-like types.
4217+
VarDecl *getHoldingVar() const;
4218+
42144219
static bool classof(const Decl *D) { return classofKind(D->getKind()); }
42154220
static bool classofKind(Kind K) { return K == Decl::Binding; }
42164221
};
@@ -4238,8 +4243,16 @@ class DecompositionDecl final
42384243
NumBindings(Bindings.size()) {
42394244
std::uninitialized_copy(Bindings.begin(), Bindings.end(),
42404245
getTrailingObjects<BindingDecl *>());
4241-
for (auto *B : Bindings)
4246+
for (auto *B : Bindings) {
42424247
B->setDecomposedDecl(this);
4248+
if (B->isParameterPack() && B->getBinding()) {
4249+
for (Expr *E : B->getBindingPackExprs()) {
4250+
auto *DRE = cast<DeclRefExpr>(E);
4251+
auto *NestedB = cast<BindingDecl>(DRE->getDecl());
4252+
NestedB->setDecomposedDecl(this);
4253+
}
4254+
}
4255+
}
42434256
}
42444257

42454258
void anchor() override;
@@ -4257,8 +4270,33 @@ class DecompositionDecl final
42574270
static DecompositionDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID,
42584271
unsigned NumBindings);
42594272

4260-
ArrayRef<BindingDecl *> bindings() const {
4261-
return llvm::ArrayRef(getTrailingObjects<BindingDecl *>(), NumBindings);
4273+
// Provide the range of bindings which may have a nested pack.
4274+
llvm::ArrayRef<BindingDecl *> bindings() const {
4275+
return {getTrailingObjects<BindingDecl *>(), NumBindings};
4276+
}
4277+
4278+
// Provide a flattened range to visit each binding.
4279+
auto flat_bindings() const {
4280+
llvm::ArrayRef<BindingDecl *> Bindings = bindings();
4281+
llvm::ArrayRef<Expr *> PackExprs;
4282+
4283+
// Split the bindings into subranges split by the pack.
4284+
auto S1 = Bindings.take_until(
4285+
[](BindingDecl *BD) { return BD->isParameterPack(); });
4286+
4287+
Bindings = Bindings.drop_front(S1.size());
4288+
if (!Bindings.empty()) {
4289+
PackExprs = Bindings.front()->getBindingPackExprs();
4290+
Bindings = Bindings.drop_front();
4291+
}
4292+
4293+
auto S2 = llvm::map_range(PackExprs, [](Expr *E) {
4294+
auto *DRE = cast<DeclRefExpr>(E);
4295+
return cast<BindingDecl>(DRE->getDecl());
4296+
});
4297+
4298+
return llvm::concat<BindingDecl *>(std::move(S1), std::move(S2),
4299+
std::move(Bindings));
42624300
}
42634301

42644302
void printName(raw_ostream &OS, const PrintingPolicy &Policy) const override;

clang/include/clang/AST/ExprCXX.h

+53
Original file line numberDiff line numberDiff line change
@@ -5319,6 +5319,59 @@ class BuiltinBitCastExpr final
53195319
}
53205320
};
53215321

5322+
// Represents an unexpanded pack where the list of expressions are
5323+
// known. These are used when structured bindings introduce a pack.
5324+
class ResolvedUnexpandedPackExpr final
5325+
: public Expr,
5326+
private llvm::TrailingObjects<ResolvedUnexpandedPackExpr, Expr *> {
5327+
friend class ASTStmtReader;
5328+
friend class ASTStmtWriter;
5329+
friend TrailingObjects;
5330+
5331+
SourceLocation BeginLoc;
5332+
unsigned NumExprs;
5333+
5334+
ResolvedUnexpandedPackExpr(SourceLocation BL, QualType QT, unsigned NumExprs);
5335+
5336+
public:
5337+
static ResolvedUnexpandedPackExpr *CreateDeserialized(ASTContext &C,
5338+
unsigned NumExprs);
5339+
static ResolvedUnexpandedPackExpr *
5340+
Create(ASTContext &C, SourceLocation BeginLoc, QualType T, unsigned NumExprs);
5341+
static ResolvedUnexpandedPackExpr *Create(ASTContext &C,
5342+
SourceLocation BeginLoc, QualType T,
5343+
llvm::ArrayRef<Expr *> Exprs);
5344+
5345+
unsigned getNumExprs() const { return NumExprs; }
5346+
5347+
llvm::MutableArrayRef<Expr *> getExprs() {
5348+
return {getTrailingObjects<Expr *>(), NumExprs};
5349+
}
5350+
5351+
llvm::ArrayRef<Expr *> getExprs() const {
5352+
return {getTrailingObjects<Expr *>(), NumExprs};
5353+
}
5354+
5355+
Expr *getExpansion(unsigned Idx) { return getExprs()[Idx]; }
5356+
Expr *getExpansion(unsigned Idx) const { return getExprs()[Idx]; }
5357+
5358+
// Iterators
5359+
child_range children() {
5360+
return child_range((Stmt **)getTrailingObjects<Expr *>(),
5361+
(Stmt **)getTrailingObjects<Expr *>() + getNumExprs());
5362+
}
5363+
5364+
SourceLocation getBeginLoc() const LLVM_READONLY { return BeginLoc; }
5365+
SourceLocation getEndLoc() const LLVM_READONLY { return BeginLoc; }
5366+
5367+
// Returns the resolved pack of a decl or nullptr
5368+
static ResolvedUnexpandedPackExpr *getFromDecl(Decl *);
5369+
5370+
static bool classof(const Stmt *T) {
5371+
return T->getStmtClass() == ResolvedUnexpandedPackExprClass;
5372+
}
5373+
};
5374+
53225375
} // namespace clang
53235376

53245377
#endif // LLVM_CLANG_AST_EXPRCXX_H

clang/include/clang/AST/RecursiveASTVisitor.h

+1
Original file line numberDiff line numberDiff line change
@@ -2950,6 +2950,7 @@ DEF_TRAVERSE_STMT(FunctionParmPackExpr, {})
29502950
DEF_TRAVERSE_STMT(CXXFoldExpr, {})
29512951
DEF_TRAVERSE_STMT(AtomicExpr, {})
29522952
DEF_TRAVERSE_STMT(CXXParenListInitExpr, {})
2953+
DEF_TRAVERSE_STMT(ResolvedUnexpandedPackExpr, {})
29532954

29542955
DEF_TRAVERSE_STMT(MaterializeTemporaryExpr, {
29552956
if (S->getLifetimeExtendedTemporaryDecl()) {

clang/include/clang/Basic/DiagnosticParseKinds.td

+10
Original file line numberDiff line numberDiff line change
@@ -1099,6 +1099,16 @@ def err_lambda_capture_misplaced_ellipsis : Error<
10991099
"the name of the capture">;
11001100
def err_lambda_capture_multiple_ellipses : Error<
11011101
"multiple ellipses in pack capture">;
1102+
def err_binding_multiple_ellipses : Error<
1103+
"multiple packs in structured binding declaration">;
1104+
def note_previous_ellipsis : Note<
1105+
"previous binding pack specified here">;
1106+
def ext_cxx_binding_pack : ExtWarn<
1107+
"structured binding packs are a C++2c extension ">,
1108+
InGroup<CXX26>;
1109+
def warn_cxx23_compat_binding_pack : Warning<
1110+
"structured binding packs are incompatible with C++ standards before C++2c">,
1111+
InGroup<CXXPre26Compat>, DefaultIgnore;
11021112
def err_capture_default_first : Error<
11031113
"capture default must be first">;
11041114
def ext_decl_attrs_on_lambda : ExtWarn<

clang/include/clang/Basic/DiagnosticSemaKinds.td

+3
Original file line numberDiff line numberDiff line change
@@ -5945,6 +5945,9 @@ def warn_cxx23_pack_indexing : Warning<
59455945
"pack indexing is incompatible with C++ standards before C++2c">,
59465946
DefaultIgnore, InGroup<CXXPre26Compat>;
59475947

5948+
def err_pack_outside_template : Error<
5949+
"pack declaration outside of template">;
5950+
59485951
def err_fold_expression_packs_both_sides : Error<
59495952
"binary fold expression has unexpanded parameter packs in both operands">;
59505953
def err_fold_expression_empty : Error<

clang/include/clang/Basic/StmtNodes.td

+1
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ def MaterializeTemporaryExpr : StmtNode<Expr>;
163163
def LambdaExpr : StmtNode<Expr>;
164164
def CXXFoldExpr : StmtNode<Expr>;
165165
def CXXParenListInitExpr: StmtNode<Expr>;
166+
def ResolvedUnexpandedPackExpr : StmtNode<Expr>;
166167

167168
// C++ Coroutines expressions
168169
def CoroutineSuspendExpr : StmtNode<Expr, 1>;

clang/include/clang/Sema/DeclSpec.h

+1
Original file line numberDiff line numberDiff line change
@@ -1795,6 +1795,7 @@ class DecompositionDeclarator {
17951795
IdentifierInfo *Name;
17961796
SourceLocation NameLoc;
17971797
std::optional<ParsedAttributes> Attrs;
1798+
SourceLocation EllipsisLoc;
17981799
};
17991800

18001801
private:

clang/include/clang/Sema/Sema.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,8 @@ void threadSafetyCleanup(BeforeSet *Cache);
232232

233233
// FIXME: No way to easily map from TemplateTypeParmTypes to
234234
// TemplateTypeParmDecls, so we have this horrible PointerUnion.
235-
typedef std::pair<llvm::PointerUnion<const TemplateTypeParmType *, NamedDecl *>,
235+
typedef std::pair<llvm::PointerUnion<const TemplateTypeParmType *, NamedDecl *,
236+
ResolvedUnexpandedPackExpr *>,
236237
SourceLocation>
237238
UnexpandedParameterPack;
238239

@@ -6021,6 +6022,7 @@ class Sema final : public SemaBase {
60216022
RecordDecl *ClassDecl,
60226023
const IdentifierInfo *Name);
60236024

6025+
unsigned GetDecompositionElementCount(QualType DecompType);
60246026
void CheckCompleteDecompositionDeclaration(DecompositionDecl *DD);
60256027

60266028
/// Stack containing information needed when in C++2a an 'auto' is encountered

clang/include/clang/Serialization/ASTBitCodes.h

+1
Original file line numberDiff line numberDiff line change
@@ -1908,6 +1908,7 @@ enum StmtCode {
19081908
EXPR_PACK_EXPANSION, // PackExpansionExpr
19091909
EXPR_PACK_INDEXING, // PackIndexingExpr
19101910
EXPR_SIZEOF_PACK, // SizeOfPackExpr
1911+
EXPR_RESOLVED_UNEXPANDED_PACK, // ResolvedUnexpandedPackExpr
19111912
EXPR_SUBST_NON_TYPE_TEMPLATE_PARM, // SubstNonTypeTemplateParmExpr
19121913
EXPR_SUBST_NON_TYPE_TEMPLATE_PARM_PACK, // SubstNonTypeTemplateParmPackExpr
19131914
EXPR_FUNCTION_PARM_PACK, // FunctionParmPackExpr

clang/lib/AST/ASTContext.cpp

+3-2
Original file line numberDiff line numberDiff line change
@@ -12841,11 +12841,12 @@ bool ASTContext::DeclMustBeEmitted(const Decl *D) {
1284112841

1284212842
// Likewise, variables with tuple-like bindings are required if their
1284312843
// bindings have side-effects.
12844-
if (const auto *DD = dyn_cast<DecompositionDecl>(VD))
12845-
for (const auto *BD : DD->bindings())
12844+
if (const auto *DD = dyn_cast<DecompositionDecl>(VD)) {
12845+
for (const auto *BD : DD->flat_bindings())
1284612846
if (const auto *BindingVD = BD->getHoldingVar())
1284712847
if (DeclMustBeEmitted(BindingVD))
1284812848
return true;
12849+
}
1284912850

1285012851
return false;
1285112852
}

clang/lib/AST/ASTImporter.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -2553,7 +2553,7 @@ ExpectedDecl ASTNodeImporter::VisitBindingDecl(BindingDecl *D) {
25532553

25542554
BindingDecl *ToD;
25552555
if (GetImportedOrCreateDecl(ToD, D, Importer.getToContext(), DC, Loc,
2556-
Name.getAsIdentifierInfo()))
2556+
Name.getAsIdentifierInfo(), D->getType()))
25572557
return ToD;
25582558

25592559
Error Err = Error::success();

clang/lib/AST/Decl.cpp

+7-4
Original file line numberDiff line numberDiff line change
@@ -2659,10 +2659,6 @@ bool VarDecl::checkForConstantInitialization(
26592659
return Eval->HasConstantInitialization;
26602660
}
26612661

2662-
bool VarDecl::isParameterPack() const {
2663-
return isa<PackExpansionType>(getType());
2664-
}
2665-
26662662
template<typename DeclT>
26672663
static DeclT *getDefinitionOrSelf(DeclT *D) {
26682664
assert(D);
@@ -5421,6 +5417,13 @@ bool ValueDecl::isInitCapture() const {
54215417
return false;
54225418
}
54235419

5420+
bool ValueDecl::isParameterPack() const {
5421+
if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(this))
5422+
return NTTP->isParameterPack();
5423+
5424+
return isa_and_nonnull<PackExpansionType>(getType().getTypePtrOrNull());
5425+
}
5426+
54245427
void ImplicitParamDecl::anchor() {}
54255428

54265429
ImplicitParamDecl *ImplicitParamDecl::Create(ASTContext &C, DeclContext *DC,

clang/lib/AST/DeclBase.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ bool Decl::isTemplateParameterPack() const {
245245
}
246246

247247
bool Decl::isParameterPack() const {
248-
if (const auto *Var = dyn_cast<VarDecl>(this))
248+
if (const auto *Var = dyn_cast<ValueDecl>(this))
249249
return Var->isParameterPack();
250250

251251
return isTemplateParameterPack();

clang/lib/AST/DeclCXX.cpp

+12-4
Original file line numberDiff line numberDiff line change
@@ -3462,19 +3462,21 @@ VarDecl *ValueDecl::getPotentiallyDecomposedVarDecl() {
34623462
if (auto *Var = llvm::dyn_cast<VarDecl>(this))
34633463
return Var;
34643464
if (auto *BD = llvm::dyn_cast<BindingDecl>(this))
3465-
return llvm::dyn_cast<VarDecl>(BD->getDecomposedDecl());
3465+
return llvm::dyn_cast_if_present<VarDecl>(BD->getDecomposedDecl());
34663466
return nullptr;
34673467
}
34683468

34693469
void BindingDecl::anchor() {}
34703470

34713471
BindingDecl *BindingDecl::Create(ASTContext &C, DeclContext *DC,
3472-
SourceLocation IdLoc, IdentifierInfo *Id) {
3473-
return new (C, DC) BindingDecl(DC, IdLoc, Id);
3472+
SourceLocation IdLoc, IdentifierInfo *Id,
3473+
QualType T) {
3474+
return new (C, DC) BindingDecl(DC, IdLoc, Id, T);
34743475
}
34753476

34763477
BindingDecl *BindingDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID) {
3477-
return new (C, ID) BindingDecl(nullptr, SourceLocation(), nullptr);
3478+
return new (C, ID)
3479+
BindingDecl(nullptr, SourceLocation(), nullptr, QualType());
34783480
}
34793481

34803482
VarDecl *BindingDecl::getHoldingVar() const {
@@ -3490,6 +3492,12 @@ VarDecl *BindingDecl::getHoldingVar() const {
34903492
return VD;
34913493
}
34923494

3495+
llvm::ArrayRef<Expr *> BindingDecl::getBindingPackExprs() const {
3496+
assert(Binding && "expecting a pack expr");
3497+
auto *RP = cast<ResolvedUnexpandedPackExpr>(Binding);
3498+
return RP->getExprs();
3499+
}
3500+
34933501
void DecompositionDecl::anchor() {}
34943502

34953503
DecompositionDecl *DecompositionDecl::Create(ASTContext &C, DeclContext *DC,

clang/lib/AST/Expr.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -3659,6 +3659,7 @@ bool Expr::HasSideEffects(const ASTContext &Ctx,
36593659
case PackIndexingExprClass:
36603660
case HLSLOutArgExprClass:
36613661
case OpenACCAsteriskSizeExprClass:
3662+
case ResolvedUnexpandedPackExprClass:
36623663
// These never have a side-effect.
36633664
return false;
36643665

0 commit comments

Comments
 (0)