-
Notifications
You must be signed in to change notification settings - Fork 12.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Clang][P1061] Add stuctured binding packs #121417
Changes from 22 commits
3c81c5b
116aff1
66ff7c6
5720bd3
ffefb67
685f947
d957783
91ca5b4
abab5cf
80acb71
360708d
ea249ac
dda9d89
0b83b99
d496c54
d064095
97257d7
39ab76e
765db8a
8cf8205
6d49a3d
5c351d7
5302830
e497a0c
5751d2f
36f8029
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4131,31 +4131,32 @@ 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; | ||
|
||
public: | ||
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 | ||
/// in two different cases: while parsing the initializer for the | ||
/// 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()) { | ||
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,35 @@ 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()); | ||
}); | ||
|
||
// llvm::concat must take temporaries or it will capture | ||
// references. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I found this comment a bit confusing, because There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, right. I just removed the comment. |
||
return llvm::concat<BindingDecl *>(std::move(S1), std::move(S2), | ||
std::move(Bindings)); | ||
} | ||
|
||
void printName(raw_ostream &OS, const PrintingPolicy &Policy) const override; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
cor3ntin marked this conversation as resolved.
Show resolved
Hide resolved
Comment on lines
+5324
to
+5326
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems to be doing the same job as There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I definitely think they could be generalized which is what I had in mind with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we try to address that? |
||
: public Expr, | ||
private llvm::TrailingObjects<ResolvedUnexpandedPackExpr, Expr *> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we store There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably. That would simplify the DeclRefExpr stuff and a few other things. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This is what I think: using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Personally, I am content to leave it as is, but storing Decls instead of Exprs does have the benefit of delaying building the DeclRefExprs until expansion which creates less garbage and would simplify There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
A use case would be unpacking a tuple for example (or data member pack but in that case maybe we want to store decl for that too) Either way, I don't think we should predicament the design on future evolution. If we need to change it later, we can |
||
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 |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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,13 @@ VarDecl *BindingDecl::getHoldingVar() const { | |
return VD; | ||
} | ||
|
||
llvm::ArrayRef<Expr *> BindingDecl::getBindingPackExprs() const { | ||
if (!Binding) | ||
return {}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looking at how this is used, i think we can assert here instead There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added. The assert was being triggered in one spot that I realized was unnecessary (when I tried removing the whole block of code that was going back and adding types to the DeclRefExprs.) |
||
auto *RP = cast<ResolvedUnexpandedPackExpr>(Binding); | ||
return RP->getExprs(); | ||
} | ||
|
||
void DecompositionDecl::anchor() {} | ||
|
||
DecompositionDecl *DecompositionDecl::Create(ASTContext &C, DeclContext *DC, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we get the comment to move as well? Or at least an updated one?