[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 https://github.com/llvm/llvm-project/issues/54300
Fixes https://github.com/llvm/llvm-project/issues/54300
Fixes https://github.com/llvm/llvm-project/issues/52720
Reviewed By: aaron.ballman
Differential Revision: https://reviews.llvm.org/D122768
diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index 6441cd3..1b414b5 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -689,6 +689,11 @@
/// or declared with the weak or weak-ref attr.
bool isWeak() const;
+ /// Whether this variable is the implicit variable for a lambda init-capture.
+ /// Only VarDecl can be init captures, but both VarDecl and BindingDecl
+ /// can be captured.
+ bool isInitCapture() 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; }
diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h
index 9e4fc3f..c973019 100644
--- a/clang/include/clang/AST/DeclCXX.h
+++ b/clang/include/clang/AST/DeclCXX.h
@@ -1057,8 +1057,9 @@
///
/// \note No entries will be added for init-captures, as they do not capture
/// variables.
- void getCaptureFields(llvm::DenseMap<const VarDecl *, FieldDecl *> &Captures,
- FieldDecl *&ThisCapture) const;
+ void
+ getCaptureFields(llvm::DenseMap<const ValueDecl *, FieldDecl *> &Captures,
+ FieldDecl *&ThisCapture) const;
using capture_const_iterator = const LambdaCapture *;
using capture_const_range = llvm::iterator_range<capture_const_iterator>;
diff --git a/clang/include/clang/AST/LambdaCapture.h b/clang/include/clang/AST/LambdaCapture.h
index 7ad1e23..62e7716 100644
--- a/clang/include/clang/AST/LambdaCapture.h
+++ b/clang/include/clang/AST/LambdaCapture.h
@@ -71,7 +71,7 @@
/// capture that is a pack expansion, or an invalid source
/// location to indicate that this is not a pack expansion.
LambdaCapture(SourceLocation Loc, bool Implicit, LambdaCaptureKind Kind,
- VarDecl *Var = nullptr,
+ ValueDecl *Var = nullptr,
SourceLocation EllipsisLoc = SourceLocation());
/// Determine the kind of capture.
@@ -86,7 +86,7 @@
/// Determine whether this capture handles a variable.
bool capturesVariable() const {
- return isa_and_nonnull<VarDecl>(DeclAndBits.getPointer());
+ return isa_and_nonnull<ValueDecl>(DeclAndBits.getPointer());
}
/// Determine whether this captures a variable length array bound
@@ -101,9 +101,9 @@
///
/// This operation is only valid if this capture is a variable capture
/// (other than a capture of \c this).
- VarDecl *getCapturedVar() const {
+ ValueDecl *getCapturedVar() const {
assert(capturesVariable() && "No variable available for capture");
- return static_cast<VarDecl *>(DeclAndBits.getPointer());
+ return static_cast<ValueDecl *>(DeclAndBits.getPointer());
}
/// Determine whether this was an implicit capture (not
diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h
index 49a66a1..52c0ca5 100644
--- a/clang/include/clang/AST/Stmt.h
+++ b/clang/include/clang/AST/Stmt.h
@@ -59,6 +59,7 @@
class SourceManager;
class StringLiteral;
class Token;
+class ValueDecl;
class VarDecl;
//===----------------------------------------------------------------------===//
diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h
index 9f4d807..5bcf1d3 100644
--- a/clang/include/clang/ASTMatchers/ASTMatchers.h
+++ b/clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -4722,7 +4722,7 @@
/// In the matcher
/// lambdaExpr(hasAnyCapture(lambdaCapture(capturesVar(hasName("x")))),
/// capturesVar(hasName("x")) matches `x` and `x = 1`.
-AST_MATCHER_P(LambdaCapture, capturesVar, internal::Matcher<VarDecl>,
+AST_MATCHER_P(LambdaCapture, capturesVar, internal::Matcher<ValueDecl>,
InnerMatcher) {
auto *capturedVar = Node.getCapturedVar();
return capturedVar && InnerMatcher.matches(*capturedVar, Finder, Builder);
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 6ff5b8d..550ad5a 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -9013,6 +9013,16 @@
def err_reference_to_local_in_enclosing_context : Error<
"reference to local %select{variable|binding}1 %0 declared in enclosing "
"%select{%3|block literal|lambda expression|context}2">;
+def err_bitfield_capture_by_ref : Error<
+ "cannot capture a bit-field by reference">;
+def err_capture_binding_openmp : Error<
+ "capturing a structured binding is not yet supported in OpenMP">;
+def ext_capture_binding : ExtWarn<
+ "captured structured bindings are a C++20 extension">, InGroup<CXX20>;
+def warn_cxx17_compat_capture_binding : Warning<
+ "captured structured bindings are incompatible with "
+ "C++ standards before C++20">,
+ InGroup<CXXPre20Compat>, DefaultIgnore;
def err_static_data_member_not_allowed_in_local_class : Error<
"static data member %0 not allowed in local %sub{select_tag_type_kind}2 %1">;
diff --git a/clang/include/clang/Sema/ScopeInfo.h b/clang/include/clang/Sema/ScopeInfo.h
index 18848c8d..c0dfcb8 100644
--- a/clang/include/clang/Sema/ScopeInfo.h
+++ b/clang/include/clang/Sema/ScopeInfo.h
@@ -553,7 +553,7 @@
const VariableArrayType *CapturedVLA;
/// Otherwise, the captured variable (if any).
- VarDecl *CapturedVar;
+ ValueDecl *CapturedVar;
};
/// The source location at which the first capture occurred.
@@ -589,12 +589,13 @@
unsigned Invalid : 1;
public:
- Capture(VarDecl *Var, bool Block, bool ByRef, bool IsNested,
+ Capture(ValueDecl *Var, bool Block, bool ByRef, bool IsNested,
SourceLocation Loc, SourceLocation EllipsisLoc, QualType CaptureType,
bool Invalid)
: CapturedVar(Var), Loc(Loc), EllipsisLoc(EllipsisLoc),
- CaptureType(CaptureType),
- Kind(Block ? Cap_Block : ByRef ? Cap_ByRef : Cap_ByCopy),
+ CaptureType(CaptureType), Kind(Block ? Cap_Block
+ : ByRef ? Cap_ByRef
+ : Cap_ByCopy),
Nested(IsNested), CapturesThis(false), ODRUsed(false),
NonODRUsed(false), Invalid(Invalid) {}
@@ -639,7 +640,7 @@
NonODRUsed = true;
}
- VarDecl *getVariable() const {
+ ValueDecl *getVariable() const {
assert(isVariableCapture());
return CapturedVar;
}
@@ -678,7 +679,7 @@
: FunctionScopeInfo(Diag), ImpCaptureStyle(Style) {}
/// CaptureMap - A map of captured variables to (index+1) into Captures.
- llvm::DenseMap<VarDecl*, unsigned> CaptureMap;
+ llvm::DenseMap<ValueDecl *, unsigned> CaptureMap;
/// CXXThisCaptureIndex - The (index+1) of the capture of 'this';
/// zero if 'this' is not captured.
@@ -695,7 +696,7 @@
/// or null if unknown.
QualType ReturnType;
- void addCapture(VarDecl *Var, bool isBlock, bool isByref, bool isNested,
+ void addCapture(ValueDecl *Var, bool isBlock, bool isByref, bool isNested,
SourceLocation Loc, SourceLocation EllipsisLoc,
QualType CaptureType, bool Invalid) {
Captures.push_back(Capture(Var, isBlock, isByref, isNested, Loc,
@@ -722,23 +723,21 @@
}
/// Determine whether the given variable has been captured.
- bool isCaptured(VarDecl *Var) const {
- return CaptureMap.count(Var);
- }
+ bool isCaptured(ValueDecl *Var) const { return CaptureMap.count(Var); }
/// Determine whether the given variable-array type has been captured.
bool isVLATypeCaptured(const VariableArrayType *VAT) const;
/// Retrieve the capture of the given variable, if it has been
/// captured already.
- Capture &getCapture(VarDecl *Var) {
+ Capture &getCapture(ValueDecl *Var) {
assert(isCaptured(Var) && "Variable has not been captured");
return Captures[CaptureMap[Var] - 1];
}
- const Capture &getCapture(VarDecl *Var) const {
- llvm::DenseMap<VarDecl*, unsigned>::const_iterator Known
- = CaptureMap.find(Var);
+ const Capture &getCapture(ValueDecl *Var) const {
+ llvm::DenseMap<ValueDecl *, unsigned>::const_iterator Known =
+ CaptureMap.find(Var);
assert(Known != CaptureMap.end() && "Variable has not been captured");
return Captures[Known->second - 1];
}
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index ed50b9e..706383e 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -5325,23 +5325,23 @@
///
/// \returns true if an error occurred (i.e., the variable cannot be
/// captured) and false if the capture succeeded.
- bool tryCaptureVariable(VarDecl *Var, SourceLocation Loc, TryCaptureKind Kind,
- SourceLocation EllipsisLoc, bool BuildAndDiagnose,
- QualType &CaptureType,
+ bool tryCaptureVariable(ValueDecl *Var, SourceLocation Loc,
+ TryCaptureKind Kind, SourceLocation EllipsisLoc,
+ bool BuildAndDiagnose, QualType &CaptureType,
QualType &DeclRefType,
const unsigned *const FunctionScopeIndexToStopAt);
/// Try to capture the given variable.
- bool tryCaptureVariable(VarDecl *Var, SourceLocation Loc,
+ bool tryCaptureVariable(ValueDecl *Var, SourceLocation Loc,
TryCaptureKind Kind = TryCapture_Implicit,
SourceLocation EllipsisLoc = SourceLocation());
/// Checks if the variable must be captured.
- bool NeedToCaptureVariable(VarDecl *Var, SourceLocation Loc);
+ bool NeedToCaptureVariable(ValueDecl *Var, SourceLocation Loc);
/// Given a variable, determine the type that a reference to that
/// variable will have in the given scope.
- QualType getCapturedDeclRefType(VarDecl *Var, SourceLocation Loc);
+ QualType getCapturedDeclRefType(ValueDecl *Var, SourceLocation Loc);
/// Mark all of the declarations referenced within a particular AST node as
/// referenced. Used when template instantiation instantiates a non-dependent