[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