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 llvm/llvm-project#54300 Fixes llvm/llvm-project#54300 Fixes llvm/llvm-project#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

0 commit comments

Comments
 (0)