Skip to content

Commit 127bf44

Browse files
committed
[Clang][C++20] Support capturing structured bindings in lambdas
This completes the implementation of P1091R3 and P1381R1. This patch allow the capture of structured bindings both for C++20+ and C++17, with extension/compat warning. In addition, capturing an anonymous union member, a bitfield, or a structured binding thereof now has a better diagnostic. We only support structured bindings - as opposed to other kinds of structured statements/blocks. We still emit an error for those. In addition, support for structured bindings capture is entirely disabled in OpenMP mode as this needs more investigation - a specific diagnostic indicate the feature is not yet supported there. Note that the rest of P1091R3 (static/thread_local structured bindings) was already implemented. at the request of @shafik, i can confirm the correct behavior of lldb wit this change. Fixes #54300 Fixes #54300 Fixes #52720 Reviewed By: aaron.ballman Differential Revision: https://reviews.llvm.org/D122768
1 parent b6b0690 commit 127bf44

Some content is hidden

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

41 files changed

+539
-162
lines changed

clang-tools-extra/clang-tidy/modernize/LoopConvertUtils.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -785,8 +785,8 @@ bool ForLoopIndexUseVisitor::TraverseLambdaCapture(LambdaExpr *LE,
785785
const LambdaCapture *C,
786786
Expr *Init) {
787787
if (C->capturesVariable()) {
788-
const VarDecl *VDecl = C->getCapturedVar();
789-
if (areSameVariable(IndexVar, cast<ValueDecl>(VDecl))) {
788+
const ValueDecl *VDecl = C->getCapturedVar();
789+
if (areSameVariable(IndexVar, VDecl)) {
790790
// FIXME: if the index is captured, it will count as an usage and the
791791
// alias (if any) won't work, because it is only used in case of having
792792
// exactly one usage.

clang/docs/ReleaseNotes.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,16 @@ C++ Language Changes in Clang
108108
C++20 Feature Support
109109
^^^^^^^^^^^^^^^^^^^^^
110110

111+
- Support capturing structured bindings in lambdas
112+
(`P1091R3 <https://wg21.link/p1091r3>`_ and `P1381R1 <https://wg21.link/P1381R1>`).
113+
This fixes issues `GH52720 <https://github.com/llvm/llvm-project/issues/52720>`_,
114+
`GH54300 <https://github.com/llvm/llvm-project/issues/54300>`_,
115+
`GH54301 <https://github.com/llvm/llvm-project/issues/54301>`_,
116+
and `GH49430 <https://github.com/llvm/llvm-project/issues/49430>`_.
117+
118+
119+
120+
111121
C++2b Feature Support
112122
^^^^^^^^^^^^^^^^^^^^^
113123

clang/include/clang/AST/Decl.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -689,6 +689,11 @@ class ValueDecl : public NamedDecl {
689689
/// or declared with the weak or weak-ref attr.
690690
bool isWeak() const;
691691

692+
/// Whether this variable is the implicit variable for a lambda init-capture.
693+
/// Only VarDecl can be init captures, but both VarDecl and BindingDecl
694+
/// can be captured.
695+
bool isInitCapture() const;
696+
692697
// Implement isa/cast/dyncast/etc.
693698
static bool classof(const Decl *D) { return classofKind(D->getKind()); }
694699
static bool classofKind(Kind K) { return K >= firstValue && K <= lastValue; }

clang/include/clang/AST/DeclCXX.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1057,8 +1057,9 @@ class CXXRecordDecl : public RecordDecl {
10571057
///
10581058
/// \note No entries will be added for init-captures, as they do not capture
10591059
/// variables.
1060-
void getCaptureFields(llvm::DenseMap<const VarDecl *, FieldDecl *> &Captures,
1061-
FieldDecl *&ThisCapture) const;
1060+
void
1061+
getCaptureFields(llvm::DenseMap<const ValueDecl *, FieldDecl *> &Captures,
1062+
FieldDecl *&ThisCapture) const;
10621063

10631064
using capture_const_iterator = const LambdaCapture *;
10641065
using capture_const_range = llvm::iterator_range<capture_const_iterator>;

clang/include/clang/AST/LambdaCapture.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ class LambdaCapture {
7171
/// capture that is a pack expansion, or an invalid source
7272
/// location to indicate that this is not a pack expansion.
7373
LambdaCapture(SourceLocation Loc, bool Implicit, LambdaCaptureKind Kind,
74-
VarDecl *Var = nullptr,
74+
ValueDecl *Var = nullptr,
7575
SourceLocation EllipsisLoc = SourceLocation());
7676

7777
/// Determine the kind of capture.
@@ -86,7 +86,7 @@ class LambdaCapture {
8686

8787
/// Determine whether this capture handles a variable.
8888
bool capturesVariable() const {
89-
return isa_and_nonnull<VarDecl>(DeclAndBits.getPointer());
89+
return isa_and_nonnull<ValueDecl>(DeclAndBits.getPointer());
9090
}
9191

9292
/// Determine whether this captures a variable length array bound
@@ -101,9 +101,9 @@ class LambdaCapture {
101101
///
102102
/// This operation is only valid if this capture is a variable capture
103103
/// (other than a capture of \c this).
104-
VarDecl *getCapturedVar() const {
104+
ValueDecl *getCapturedVar() const {
105105
assert(capturesVariable() && "No variable available for capture");
106-
return static_cast<VarDecl *>(DeclAndBits.getPointer());
106+
return static_cast<ValueDecl *>(DeclAndBits.getPointer());
107107
}
108108

109109
/// Determine whether this was an implicit capture (not

clang/include/clang/AST/Stmt.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ class RecordDecl;
5959
class SourceManager;
6060
class StringLiteral;
6161
class Token;
62+
class ValueDecl;
6263
class VarDecl;
6364

6465
//===----------------------------------------------------------------------===//

clang/include/clang/ASTMatchers/ASTMatchers.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4722,7 +4722,7 @@ AST_MATCHER_P(LambdaExpr, hasAnyCapture, internal::Matcher<LambdaCapture>,
47224722
/// In the matcher
47234723
/// lambdaExpr(hasAnyCapture(lambdaCapture(capturesVar(hasName("x")))),
47244724
/// capturesVar(hasName("x")) matches `x` and `x = 1`.
4725-
AST_MATCHER_P(LambdaCapture, capturesVar, internal::Matcher<VarDecl>,
4725+
AST_MATCHER_P(LambdaCapture, capturesVar, internal::Matcher<ValueDecl>,
47264726
InnerMatcher) {
47274727
auto *capturedVar = Node.getCapturedVar();
47284728
return capturedVar && InnerMatcher.matches(*capturedVar, Finder, Builder);

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9013,6 +9013,16 @@ def ext_ms_anonymous_record : ExtWarn<
90139013
def err_reference_to_local_in_enclosing_context : Error<
90149014
"reference to local %select{variable|binding}1 %0 declared in enclosing "
90159015
"%select{%3|block literal|lambda expression|context}2">;
9016+
def err_bitfield_capture_by_ref : Error<
9017+
"cannot capture a bit-field by reference">;
9018+
def err_capture_binding_openmp : Error<
9019+
"capturing a structured binding is not yet supported in OpenMP">;
9020+
def ext_capture_binding : ExtWarn<
9021+
"captured structured bindings are a C++20 extension">, InGroup<CXX20>;
9022+
def warn_cxx17_compat_capture_binding : Warning<
9023+
"captured structured bindings are incompatible with "
9024+
"C++ standards before C++20">,
9025+
InGroup<CXXPre20Compat>, DefaultIgnore;
90169026

90179027
def err_static_data_member_not_allowed_in_local_class : Error<
90189028
"static data member %0 not allowed in local %sub{select_tag_type_kind}2 %1">;

clang/include/clang/Sema/ScopeInfo.h

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -553,7 +553,7 @@ class Capture {
553553
const VariableArrayType *CapturedVLA;
554554

555555
/// Otherwise, the captured variable (if any).
556-
VarDecl *CapturedVar;
556+
ValueDecl *CapturedVar;
557557
};
558558

559559
/// The source location at which the first capture occurred.
@@ -589,12 +589,13 @@ class Capture {
589589
unsigned Invalid : 1;
590590

591591
public:
592-
Capture(VarDecl *Var, bool Block, bool ByRef, bool IsNested,
592+
Capture(ValueDecl *Var, bool Block, bool ByRef, bool IsNested,
593593
SourceLocation Loc, SourceLocation EllipsisLoc, QualType CaptureType,
594594
bool Invalid)
595595
: CapturedVar(Var), Loc(Loc), EllipsisLoc(EllipsisLoc),
596-
CaptureType(CaptureType),
597-
Kind(Block ? Cap_Block : ByRef ? Cap_ByRef : Cap_ByCopy),
596+
CaptureType(CaptureType), Kind(Block ? Cap_Block
597+
: ByRef ? Cap_ByRef
598+
: Cap_ByCopy),
598599
Nested(IsNested), CapturesThis(false), ODRUsed(false),
599600
NonODRUsed(false), Invalid(Invalid) {}
600601

@@ -639,7 +640,7 @@ class Capture {
639640
NonODRUsed = true;
640641
}
641642

642-
VarDecl *getVariable() const {
643+
ValueDecl *getVariable() const {
643644
assert(isVariableCapture());
644645
return CapturedVar;
645646
}
@@ -678,7 +679,7 @@ class CapturingScopeInfo : public FunctionScopeInfo {
678679
: FunctionScopeInfo(Diag), ImpCaptureStyle(Style) {}
679680

680681
/// CaptureMap - A map of captured variables to (index+1) into Captures.
681-
llvm::DenseMap<VarDecl*, unsigned> CaptureMap;
682+
llvm::DenseMap<ValueDecl *, unsigned> CaptureMap;
682683

683684
/// CXXThisCaptureIndex - The (index+1) of the capture of 'this';
684685
/// zero if 'this' is not captured.
@@ -695,7 +696,7 @@ class CapturingScopeInfo : public FunctionScopeInfo {
695696
/// or null if unknown.
696697
QualType ReturnType;
697698

698-
void addCapture(VarDecl *Var, bool isBlock, bool isByref, bool isNested,
699+
void addCapture(ValueDecl *Var, bool isBlock, bool isByref, bool isNested,
699700
SourceLocation Loc, SourceLocation EllipsisLoc,
700701
QualType CaptureType, bool Invalid) {
701702
Captures.push_back(Capture(Var, isBlock, isByref, isNested, Loc,
@@ -722,23 +723,21 @@ class CapturingScopeInfo : public FunctionScopeInfo {
722723
}
723724

724725
/// Determine whether the given variable has been captured.
725-
bool isCaptured(VarDecl *Var) const {
726-
return CaptureMap.count(Var);
727-
}
726+
bool isCaptured(ValueDecl *Var) const { return CaptureMap.count(Var); }
728727

729728
/// Determine whether the given variable-array type has been captured.
730729
bool isVLATypeCaptured(const VariableArrayType *VAT) const;
731730

732731
/// Retrieve the capture of the given variable, if it has been
733732
/// captured already.
734-
Capture &getCapture(VarDecl *Var) {
733+
Capture &getCapture(ValueDecl *Var) {
735734
assert(isCaptured(Var) && "Variable has not been captured");
736735
return Captures[CaptureMap[Var] - 1];
737736
}
738737

739-
const Capture &getCapture(VarDecl *Var) const {
740-
llvm::DenseMap<VarDecl*, unsigned>::const_iterator Known
741-
= CaptureMap.find(Var);
738+
const Capture &getCapture(ValueDecl *Var) const {
739+
llvm::DenseMap<ValueDecl *, unsigned>::const_iterator Known =
740+
CaptureMap.find(Var);
742741
assert(Known != CaptureMap.end() && "Variable has not been captured");
743742
return Captures[Known->second - 1];
744743
}

clang/include/clang/Sema/Sema.h

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5325,23 +5325,23 @@ class Sema final {
53255325
///
53265326
/// \returns true if an error occurred (i.e., the variable cannot be
53275327
/// captured) and false if the capture succeeded.
5328-
bool tryCaptureVariable(VarDecl *Var, SourceLocation Loc, TryCaptureKind Kind,
5329-
SourceLocation EllipsisLoc, bool BuildAndDiagnose,
5330-
QualType &CaptureType,
5328+
bool tryCaptureVariable(ValueDecl *Var, SourceLocation Loc,
5329+
TryCaptureKind Kind, SourceLocation EllipsisLoc,
5330+
bool BuildAndDiagnose, QualType &CaptureType,
53315331
QualType &DeclRefType,
53325332
const unsigned *const FunctionScopeIndexToStopAt);
53335333

53345334
/// Try to capture the given variable.
5335-
bool tryCaptureVariable(VarDecl *Var, SourceLocation Loc,
5335+
bool tryCaptureVariable(ValueDecl *Var, SourceLocation Loc,
53365336
TryCaptureKind Kind = TryCapture_Implicit,
53375337
SourceLocation EllipsisLoc = SourceLocation());
53385338

53395339
/// Checks if the variable must be captured.
5340-
bool NeedToCaptureVariable(VarDecl *Var, SourceLocation Loc);
5340+
bool NeedToCaptureVariable(ValueDecl *Var, SourceLocation Loc);
53415341

53425342
/// Given a variable, determine the type that a reference to that
53435343
/// variable will have in the given scope.
5344-
QualType getCapturedDeclRefType(VarDecl *Var, SourceLocation Loc);
5344+
QualType getCapturedDeclRefType(ValueDecl *Var, SourceLocation Loc);
53455345

53465346
/// Mark all of the declarations referenced within a particular AST node as
53475347
/// referenced. Used when template instantiation instantiates a non-dependent

clang/lib/AST/ASTImporter.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1006,7 +1006,7 @@ ASTNodeImporter::import(const Designator &D) {
10061006

10071007
template <>
10081008
Expected<LambdaCapture> ASTNodeImporter::import(const LambdaCapture &From) {
1009-
VarDecl *Var = nullptr;
1009+
ValueDecl *Var = nullptr;
10101010
if (From.capturesVariable()) {
10111011
if (auto VarOrErr = import(From.getCapturedVar()))
10121012
Var = *VarOrErr;

clang/lib/AST/Decl.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4983,6 +4983,12 @@ bool ValueDecl::isWeak() const {
49834983
MostRecent->hasAttr<WeakRefAttr>() || isWeakImported();
49844984
}
49854985

4986+
bool ValueDecl::isInitCapture() const {
4987+
if (auto *Var = llvm::dyn_cast<VarDecl>(this))
4988+
return Var->isInitCapture();
4989+
return false;
4990+
}
4991+
49864992
void ImplicitParamDecl::anchor() {}
49874993

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

clang/lib/AST/DeclCXX.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1570,8 +1570,8 @@ CXXMethodDecl *CXXRecordDecl::getLambdaStaticInvoker(CallingConv CC) const {
15701570
}
15711571

15721572
void CXXRecordDecl::getCaptureFields(
1573-
llvm::DenseMap<const VarDecl *, FieldDecl *> &Captures,
1574-
FieldDecl *&ThisCapture) const {
1573+
llvm::DenseMap<const ValueDecl *, FieldDecl *> &Captures,
1574+
FieldDecl *&ThisCapture) const {
15751575
Captures.clear();
15761576
ThisCapture = nullptr;
15771577

clang/lib/AST/ExprCXX.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1087,7 +1087,7 @@ CXXConstructExpr::CXXConstructExpr(StmtClass SC, EmptyShell Empty,
10871087
: Expr(SC, Empty), NumArgs(NumArgs) {}
10881088

10891089
LambdaCapture::LambdaCapture(SourceLocation Loc, bool Implicit,
1090-
LambdaCaptureKind Kind, VarDecl *Var,
1090+
LambdaCaptureKind Kind, ValueDecl *Var,
10911091
SourceLocation EllipsisLoc)
10921092
: DeclAndBits(Var, 0), Loc(Loc), EllipsisLoc(EllipsisLoc) {
10931093
unsigned Bits = 0;
@@ -1211,8 +1211,8 @@ const CompoundStmt *LambdaExpr::getCompoundStmtBody() const {
12111211
}
12121212

12131213
bool LambdaExpr::isInitCapture(const LambdaCapture *C) const {
1214-
return (C->capturesVariable() && C->getCapturedVar()->isInitCapture() &&
1215-
(getCallOperator() == C->getCapturedVar()->getDeclContext()));
1214+
return C->capturesVariable() && C->getCapturedVar()->isInitCapture() &&
1215+
getCallOperator() == C->getCapturedVar()->getDeclContext();
12161216
}
12171217

12181218
LambdaExpr::capture_iterator LambdaExpr::capture_begin() const {

clang/lib/AST/ExprConstant.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -578,7 +578,7 @@ namespace {
578578

579579
/// LambdaCaptureFields - Mapping from captured variables/this to
580580
/// corresponding data members in the closure class.
581-
llvm::DenseMap<const VarDecl *, FieldDecl *> LambdaCaptureFields;
581+
llvm::DenseMap<const ValueDecl *, FieldDecl *> LambdaCaptureFields;
582582
FieldDecl *LambdaThisCaptureField;
583583

584584
CallStackFrame(EvalInfo &Info, SourceLocation CallLoc,

clang/lib/AST/StmtPrinter.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2164,7 +2164,8 @@ void StmtPrinter::VisitLambdaExpr(LambdaExpr *Node) {
21642164
OS << "...";
21652165

21662166
if (Node->isInitCapture(C)) {
2167-
VarDecl *D = C->getCapturedVar();
2167+
// Init captures are always VarDecl.
2168+
auto *D = cast<VarDecl>(C->getCapturedVar());
21682169

21692170
llvm::StringRef Pre;
21702171
llvm::StringRef Post;

clang/lib/Analysis/AnalysisDeclContext.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ bool AnalysisDeclContext::isBodyAutosynthesizedFromModelFile() const {
142142

143143
/// Returns true if \param VD is an Objective-C implicit 'self' parameter.
144144
static bool isSelfDecl(const VarDecl *VD) {
145-
return isa<ImplicitParamDecl>(VD) && VD->getName() == "self";
145+
return isa_and_nonnull<ImplicitParamDecl>(VD) && VD->getName() == "self";
146146
}
147147

148148
const ImplicitParamDecl *AnalysisDeclContext::getSelfDecl() const {
@@ -169,8 +169,8 @@ const ImplicitParamDecl *AnalysisDeclContext::getSelfDecl() const {
169169
if (!LC.capturesVariable())
170170
continue;
171171

172-
VarDecl *VD = LC.getCapturedVar();
173-
if (isSelfDecl(VD))
172+
ValueDecl *VD = LC.getCapturedVar();
173+
if (isSelfDecl(dyn_cast<VarDecl>(VD)))
174174
return dyn_cast<ImplicitParamDecl>(VD);
175175
}
176176

clang/lib/CodeGen/CGDebugInfo.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1497,7 +1497,7 @@ void CGDebugInfo::CollectRecordLambdaFields(
14971497
if (C.capturesVariable()) {
14981498
SourceLocation Loc = C.getLocation();
14991499
assert(!Field->isBitField() && "lambdas don't have bitfield members!");
1500-
VarDecl *V = C.getCapturedVar();
1500+
ValueDecl *V = C.getCapturedVar();
15011501
StringRef VName = V->getName();
15021502
llvm::DIFile *VUnit = getOrCreateFile(Loc);
15031503
auto Align = getDeclAlignIfRequired(V, CGM.getContext());

clang/lib/CodeGen/CGExpr.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2942,8 +2942,13 @@ LValue CodeGenFunction::EmitDeclRefLValue(const DeclRefExpr *E) {
29422942
// FIXME: While we're emitting a binding from an enclosing scope, all other
29432943
// DeclRefExprs we see should be implicitly treated as if they also refer to
29442944
// an enclosing scope.
2945-
if (const auto *BD = dyn_cast<BindingDecl>(ND))
2945+
if (const auto *BD = dyn_cast<BindingDecl>(ND)) {
2946+
if (E->refersToEnclosingVariableOrCapture()) {
2947+
auto *FD = LambdaCaptureFields.lookup(BD);
2948+
return EmitCapturedFieldLValue(*this, FD, CXXABIThisValue);
2949+
}
29462950
return EmitLValue(BD->getBinding());
2951+
}
29472952

29482953
// We can form DeclRefExprs naming GUID declarations when reconstituting
29492954
// non-type template parameters into expressions.

clang/lib/CodeGen/CGOpenMPRuntime.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,7 @@ class CGOpenMPInnerExprInfo final : public CGOpenMPInlinedRegionInfo {
409409
/// RAII for emitting code of OpenMP constructs.
410410
class InlinedOpenMPRegionRAII {
411411
CodeGenFunction &CGF;
412-
llvm::DenseMap<const VarDecl *, FieldDecl *> LambdaCaptureFields;
412+
llvm::DenseMap<const ValueDecl *, FieldDecl *> LambdaCaptureFields;
413413
FieldDecl *LambdaThisCaptureField = nullptr;
414414
const CodeGen::CGBlockInfo *BlockInfo = nullptr;
415415
bool NoInheritance = false;
@@ -8948,7 +8948,7 @@ class MappableExprsHandler {
89488948
Address VDAddr(Arg, CGF.ConvertTypeForMem(VDType),
89498949
CGF.getContext().getDeclAlign(VD));
89508950
LValue VDLVal = CGF.MakeAddrLValue(VDAddr, VDType);
8951-
llvm::DenseMap<const VarDecl *, FieldDecl *> Captures;
8951+
llvm::DenseMap<const ValueDecl *, FieldDecl *> Captures;
89528952
FieldDecl *ThisCapture = nullptr;
89538953
RD->getCaptureFields(Captures, ThisCapture);
89548954
if (ThisCapture) {
@@ -8970,7 +8970,7 @@ class MappableExprsHandler {
89708970
for (const LambdaCapture &LC : RD->captures()) {
89718971
if (!LC.capturesVariable())
89728972
continue;
8973-
const VarDecl *VD = LC.getCapturedVar();
8973+
const VarDecl *VD = cast<VarDecl>(LC.getCapturedVar());
89748974
if (LC.getCaptureKind() != LCK_ByRef && !VD->getType()->isPointerType())
89758975
continue;
89768976
auto It = Captures.find(VD);

0 commit comments

Comments
 (0)