cor3ntin updated this revision to Diff 440912.
cor3ntin added a comment.
- fix typo
- add decompositions tests for the array and tuuple cases
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D122768/new/
https://reviews.llvm.org/D122768
Files:
clang-tools-extra/clang-tidy/modernize/LoopConvertUtils.cpp
clang/docs/ReleaseNotes.rst
clang/include/clang/AST/DeclCXX.h
clang/include/clang/AST/LambdaCapture.h
clang/include/clang/AST/Stmt.h
clang/include/clang/ASTMatchers/ASTMatchers.h
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/include/clang/Sema/ScopeInfo.h
clang/include/clang/Sema/Sema.h
clang/lib/AST/ASTImporter.cpp
clang/lib/AST/DeclCXX.cpp
clang/lib/AST/ExprCXX.cpp
clang/lib/AST/ExprConstant.cpp
clang/lib/AST/StmtPrinter.cpp
clang/lib/Analysis/AnalysisDeclContext.cpp
clang/lib/CodeGen/CGDebugInfo.cpp
clang/lib/CodeGen/CGExpr.cpp
clang/lib/CodeGen/CGOpenMPRuntime.cpp
clang/lib/CodeGen/CGOpenMPRuntimeGPU.cpp
clang/lib/CodeGen/CodeGenFunction.h
clang/lib/Sema/ScopeInfo.cpp
clang/lib/Sema/SemaDecl.cpp
clang/lib/Sema/SemaExpr.cpp
clang/lib/Sema/SemaInit.cpp
clang/lib/Sema/SemaLambda.cpp
clang/lib/Sema/SemaOpenMP.cpp
clang/lib/Sema/SemaStmt.cpp
clang/lib/Sema/TreeTransform.h
clang/lib/Serialization/ASTWriter.cpp
clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp
clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp
clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp
clang/test/CodeGenCXX/cxx20-decomposition.cpp
clang/test/SemaCXX/cxx1z-decomposition.cpp
clang/test/SemaCXX/cxx20-decomposition.cpp
clang/test/SemaCXX/decomposition-blocks.cpp
clang/test/SemaCXX/decomposition-openmp.cpp
clang/tools/libclang/CIndex.cpp
clang/www/cxx_status.html
Index: clang/www/cxx_status.html
===================================================================
--- clang/www/cxx_status.html
+++ clang/www/cxx_status.html
@@ -1144,7 +1144,7 @@
<tr>
<td rowspan="2">Structured binding extensions</td>
<td><a href="https://wg21.link/p1091r3">P1091R3</a></td>
- <td rowspan="2" class="partial" align="center">Partial</td>
+ <td rowspan="2" class="unreleased" align="center">Clang 15</td>
</tr>
<tr>
<td><a href="https://wg21.link/p1381r1">P1381R1</a></td>
Index: clang/tools/libclang/CIndex.cpp
===================================================================
--- clang/tools/libclang/CIndex.cpp
+++ clang/tools/libclang/CIndex.cpp
@@ -3468,9 +3468,11 @@
C != CEnd; ++C) {
if (!C->capturesVariable())
continue;
-
- if (Visit(MakeCursorVariableRef(C->getCapturedVar(), C->getLocation(),
- TU)))
+ // TODO: handle structured bindings here ?
+ if (!isa<VarDecl>(C->getCapturedVar()))
+ continue;
+ if (Visit(MakeCursorVariableRef(cast<VarDecl>(C->getCapturedVar()),
+ C->getLocation(), TU)))
return true;
}
// Visit init captures
Index: clang/test/SemaCXX/decomposition-openmp.cpp
===================================================================
--- /dev/null
+++ clang/test/SemaCXX/decomposition-openmp.cpp
@@ -0,0 +1,13 @@
+
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++20 -fopenmp %s
+
+// FIXME: OpenMP should support capturing structured bindings
+auto f() {
+ int i[2] = {};
+ auto [a, b] = i; // expected-note 2{{declared here}}
+ return [=, &a] {
+ // expected-error@-1 {{capturing a structured binding is not yet supported in OpenMP}}
+ return a + b;
+ // expected-error@-1 {{capturing a structured binding is not yet supported in OpenMP}}
+ };
+}
Index: clang/test/SemaCXX/decomposition-blocks.cpp
===================================================================
--- /dev/null
+++ clang/test/SemaCXX/decomposition-blocks.cpp
@@ -0,0 +1,14 @@
+// RUN: %clang_cc1 -std=c++17 -fsyntax-only -verify %s -fblocks
+
+struct S {
+ int i : 1;
+ int j;
+};
+
+void run(void (^)());
+void test() {
+ auto [i, j] = S{1, 42}; // expected-note {{'i' declared here}}
+ run(^{
+ (void)i; // expected-error {{reference to local binding 'i' declared in enclosing function 'test'}}
+ });
+}
Index: clang/test/SemaCXX/cxx20-decomposition.cpp
===================================================================
--- /dev/null
+++ clang/test/SemaCXX/cxx20-decomposition.cpp
@@ -0,0 +1,141 @@
+// RUN: %clang_cc1 -fsyntax-only -std=c++20 -verify %s
+// expected-no-diagnostics
+
+template <typename, typename>
+constexpr bool is_same = false;
+template <typename T>
+constexpr bool is_same<T, T> = true;
+
+struct S {
+ int i;
+ int &j;
+};
+
+void check_category() {
+ int a = 42;
+ {
+ auto [v, r] = S{1, a};
+ (void)[ v, r ] {
+ static_assert(is_same<decltype(v), int>);
+ static_assert(is_same<decltype(r), int &>);
+ };
+ }
+ {
+ auto [v, r] = S{1, a};
+ (void)[&v, &r ] {
+ static_assert(is_same<decltype(v), int>);
+ static_assert(is_same<decltype(r), int &>);
+ };
+ }
+ {
+ S s{1, a};
+ const auto &[v, r] = s;
+ (void)[ v, r ] {
+ static_assert(is_same<decltype(v), const int>);
+ static_assert(is_same<decltype(r), int &>);
+ };
+ }
+ {
+ S s{1, a};
+ const auto &[v, r] = s;
+ (void)[&v, &r ] {
+ static_assert(is_same<decltype(v), const int>);
+ static_assert(is_same<decltype(r), int &>);
+ };
+ }
+}
+
+void check_array() {
+ int arr[2] = {42, 42};
+ auto &[a, b] = arr;
+ (void)[ a, &b ] {
+ static_assert(is_same<decltype(a), int>);
+ static_assert(is_same<decltype(b), int>);
+ };
+}
+
+struct tuple {
+ template <unsigned long I>
+ decltype(auto) get() {
+ if constexpr (I == 0) {
+ return a;
+ } else {
+ return b;
+ }
+ }
+
+ template <unsigned long I>
+ decltype(auto) get() const {
+ if constexpr (I == 0) {
+ return a;
+ } else {
+ return b;
+ }
+ }
+
+ int a = 0;
+ int &b = a;
+};
+
+namespace std {
+
+template <typename T>
+struct tuple_size {
+ static constexpr unsigned long value = 2;
+};
+
+template <unsigned long, typename T>
+struct tuple_element;
+
+template <>
+struct tuple_element<0, tuple> {
+ using type = int;
+};
+
+template <>
+struct tuple_element<1, tuple> {
+ using type = int &;
+};
+
+template <>
+struct tuple_element<0, const tuple> {
+ using type = int;
+};
+
+template <>
+struct tuple_element<1, const tuple> {
+ using type = const int &;
+};
+} // namespace std
+
+void check_tuple_like() {
+ tuple t;
+ {
+ auto [v, r] = t;
+ (void)[ v, r ] {
+ static_assert(is_same<decltype(v), int>);
+ static_assert(is_same<decltype(r), int &>);
+ };
+ }
+ {
+ auto &[v, r] = t;
+ (void)[&v, &r ] {
+ static_assert(is_same<decltype(v), int>);
+ static_assert(is_same<decltype(r), int &>);
+ };
+ }
+ {
+ const auto &[v, r] = t;
+ (void)[ v, r ] {
+ static_assert(is_same<decltype(v), int>);
+ static_assert(is_same<decltype(r), const int &>);
+ };
+ }
+ {
+ const auto &[v, r] = t;
+ (void)[&v, &r ] {
+ static_assert(is_same<decltype(v), int>);
+ static_assert(is_same<decltype(r), const int &>);
+ };
+ }
+}
Index: clang/test/SemaCXX/cxx1z-decomposition.cpp
===================================================================
--- clang/test/SemaCXX/cxx1z-decomposition.cpp
+++ clang/test/SemaCXX/cxx1z-decomposition.cpp
@@ -1,4 +1,5 @@
-// RUN: %clang_cc1 -std=c++17 -verify %s
+// RUN: %clang_cc1 -std=c++17 -Wc++20-extensions -verify=expected %s
+// RUN: %clang_cc1 -std=c++20 -Wpre-c++20-compat -verify=expected %s
void use_from_own_init() {
auto [a] = a; // expected-error {{binding 'a' cannot appear in the initializer of its own decomposition declaration}}
@@ -46,25 +47,58 @@
}
static_assert(f({1, 2}) == 12);
-constexpr bool g(S &&s) {
+constexpr bool g(S &&s) {
auto &[a, b] = s;
return &a == &s.a && &b == &s.b && &a != &b;
}
static_assert(g({1, 2}));
-auto [outer1, outer2] = S{1, 2};
+struct S1 {
+ int a, b;
+};
+struct S2 {
+ int a : 1; // expected-note 2{{bit-field is declared here}}
+ int b;
+};
+
+auto [outer1, outer2] = S1{1, 2};
+auto [outerbit1, outerbit2] = S1{1, 2}; // expected-note {{declared here}}
+
void enclosing() {
struct S { int a = outer1; };
- auto [n] = S(); // expected-note 2{{'n' declared here}}
+ auto [n] = S(); // expected-note 3{{'n' declared here}}
+
+ struct Q {
+ int f() { return n; } // expected-error {{reference to local binding 'n' declared in enclosing function 'enclosing'}}
+ };
+
+ (void)[&] { return n; }; // expected-warning {{C++20}}
+ (void)[n] { return n; }; // expected-warning {{C++20}}
- struct Q { int f() { return n; } }; // expected-error {{reference to local binding 'n' declared in enclosing function}}
- (void) [&] { return n; }; // expected-error {{reference to local binding 'n' declared in enclosing function}}
- (void) [n] {}; // expected-error {{'n' in capture list does not name a variable}}
+ static auto [m] = S(); // expected-note {{'m' declared here}} \
+ // expected-warning {{C++20}}
- static auto [m] = S(); // expected-warning {{extension}}
struct R { int f() { return m; } };
(void) [&] { return m; };
- (void) [m] {}; // expected-error {{'m' in capture list does not name a variable}}
+ (void)[m]{}; // expected-error {{'m' cannot be captured because it does not have automatic storage duration}}
+
+ (void)[outerbit1]{}; // expected-error {{'outerbit1' cannot be captured because it does not have automatic storage duration}}
+
+ auto [bit, var] = S2{1, 1}; // expected-note 4{{'bit' declared here}}
+
+ (void)[&bit] { // expected-error {{cannot capture a bit-field by reference}} \
+ // expected-warning {{C++20}}
+ return bit;
+ };
+
+ union { // expected-note {{declared here}}
+ int u;
+ };
+
+ (void)[&] { return bit + u; } // expected-error {{unnamed variable cannot be implicitly captured in a lambda expression}} \
+ // expected-error {{cannot capture a bit-field by reference}} \
+ // expected-warning {{C++20}}
+ ();
}
void bitfield() {
@@ -98,7 +132,7 @@
struct PR37352 {
int n;
- void f() { static auto [a] = *this; } // expected-warning {{C++20 extension}}
+ void f() { static auto [a] = *this; } // expected-warning {{C++20}}
};
namespace instantiate_template {
Index: clang/test/CodeGenCXX/cxx20-decomposition.cpp
===================================================================
--- /dev/null
+++ clang/test/CodeGenCXX/cxx20-decomposition.cpp
@@ -0,0 +1,40 @@
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck %s
+
+struct S {
+ int i;
+ int j;
+};
+
+int f() {
+ auto [i, j] = S{1, 42};
+ return [j, &i] {
+ i++;
+ return i + j;
+ }();
+}
+
+// CHECK-LABEL: define{{.*}} i32 @_Z1fv()
+// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %0, ptr align 4 @__const._Z1fv., i64 8, i1 false)
+// CHECK: %1 = getelementptr inbounds %class.anon, ptr %ref.tmp, i32 0, i32 0
+// CHECK: %j = getelementptr inbounds %struct.S, ptr %0, i32 0, i32 1
+// CHECK: %2 = load i32, ptr %j, align 4
+// CHECK: store i32 %2, ptr %1, align 8
+// CHECK: %3 = getelementptr inbounds %class.anon, ptr %ref.tmp, i32 0, i32 1
+// CHECK: %i = getelementptr inbounds %struct.S, ptr %0, i32 0, i32 0
+// CHECK: store ptr %i, ptr %3, align 8
+// CHECK: %call = call noundef i32 @"_ZZ1fvENK3$_0clEv"(ptr noundef nonnull align 8 dereferenceable(16) %ref.tmp)
+// CHECK: ret i32 %call
+
+// CHECK-LABEL: define{{.*}} i32 @"_ZZ1fvENK3$_0clEv"(
+// CHECK: %0 = getelementptr inbounds %class.anon, ptr %this1, i32 0, i32 1
+// CHECK: %1 = load ptr, ptr %0, align 8
+// CHECK: %2 = load i32, ptr %1, align 4
+// CHECK: %inc = add nsw i32 %2, 1
+// CHECK: store i32 %inc, ptr %1, align 4
+// CHECK: %3 = getelementptr inbounds %class.anon, ptr %this1, i32 0, i32 1
+// CHECK: %4 = load ptr, ptr %3, align 8
+// CHECK: %5 = load i32, ptr %4, align 4
+// CHECK: %6 = getelementptr inbounds %class.anon, ptr %this1, i32 0, i32 0
+// CHECK: %7 = load i32, ptr %6, align 8
+// CHECK: %add = add nsw i32 %5, %7
+// CHECK: ret i32 %add
Index: clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp
+++ clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp
@@ -175,7 +175,7 @@
const CXXRecordDecl *LambdaCXXRec = MD->getParent();
// Lookup the fields of the lambda
- llvm::DenseMap<const VarDecl *, FieldDecl *> LambdaCaptureFields;
+ llvm::DenseMap<const ValueDecl *, FieldDecl *> LambdaCaptureFields;
FieldDecl *LambdaThisCaptureField;
LambdaCXXRec->getCaptureFields(LambdaCaptureFields, LambdaThisCaptureField);
Index: clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -2547,7 +2547,7 @@
MD->getParent()->isLambda()) {
// Lookup the field of the lambda.
const CXXRecordDecl *CXXRec = MD->getParent();
- llvm::DenseMap<const VarDecl *, FieldDecl *> LambdaCaptureFields;
+ llvm::DenseMap<const ValueDecl *, FieldDecl *> LambdaCaptureFields;
FieldDecl *LambdaThisCaptureField;
CXXRec->getCaptureFields(LambdaCaptureFields, LambdaThisCaptureField);
Index: clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp
+++ clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp
@@ -57,7 +57,7 @@
void visitLambdaExpr(LambdaExpr *L) const {
for (const LambdaCapture &C : L->captures()) {
if (C.capturesVariable()) {
- VarDecl *CapturedVar = C.getCapturedVar();
+ ValueDecl *CapturedVar = C.getCapturedVar();
if (auto *CapturedVarType = CapturedVar->getType().getTypePtrOrNull()) {
Optional<bool> IsUncountedPtr = isUncountedPtr(CapturedVarType);
if (IsUncountedPtr && *IsUncountedPtr) {
@@ -68,7 +68,7 @@
}
}
- void reportBug(const LambdaCapture &Capture, VarDecl *CapturedVar,
+ void reportBug(const LambdaCapture &Capture, ValueDecl *CapturedVar,
const Type *T) const {
assert(CapturedVar);
Index: clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp
+++ clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp
@@ -504,7 +504,7 @@
// Treat local variables captured by reference in C++ lambdas as escaped.
void findLambdaReferenceCaptures(const LambdaExpr *LE) {
const CXXRecordDecl *LambdaClass = LE->getLambdaClass();
- llvm::DenseMap<const VarDecl *, FieldDecl *> CaptureFields;
+ llvm::DenseMap<const ValueDecl *, FieldDecl *> CaptureFields;
FieldDecl *ThisCaptureField;
LambdaClass->getCaptureFields(CaptureFields, ThisCaptureField);
@@ -512,14 +512,14 @@
if (!C.capturesVariable())
continue;
- VarDecl *VD = C.getCapturedVar();
+ ValueDecl *VD = C.getCapturedVar();
const FieldDecl *FD = CaptureFields[VD];
- if (!FD)
+ if (!FD || !isa<VarDecl>(VD))
continue;
// If the capture field is a reference type, it is capture-by-reference.
if (FD->getType()->isReferenceType())
- Escaped.insert(VD);
+ Escaped.insert(cast<VarDecl>(VD));
}
}
};
Index: clang/lib/Serialization/ASTWriter.cpp
===================================================================
--- clang/lib/Serialization/ASTWriter.cpp
+++ clang/lib/Serialization/ASTWriter.cpp
@@ -5781,7 +5781,7 @@
break;
case LCK_ByCopy:
case LCK_ByRef:
- VarDecl *Var =
+ ValueDecl *Var =
Capture.capturesVariable() ? Capture.getCapturedVar() : nullptr;
AddDeclRef(Var);
AddSourceLocation(Capture.isPackExpansion() ? Capture.getEllipsisLoc()
Index: clang/lib/Sema/TreeTransform.h
===================================================================
--- clang/lib/Sema/TreeTransform.h
+++ clang/lib/Sema/TreeTransform.h
@@ -12963,7 +12963,7 @@
continue;
TransformedInitCapture &Result = InitCaptures[C - E->capture_begin()];
- VarDecl *OldVD = C->getCapturedVar();
+ VarDecl *OldVD = cast<VarDecl>(C->getCapturedVar());
auto SubstInitCapture = [&](SourceLocation EllipsisLoc,
Optional<unsigned> NumExpansions) {
@@ -12980,7 +12980,8 @@
getSema().buildLambdaInitCaptureInitialization(
C->getLocation(), OldVD->getType()->isReferenceType(),
EllipsisLoc, NumExpansions, OldVD->getIdentifier(),
- C->getCapturedVar()->getInitStyle() != VarDecl::CInit,
+ cast<VarDecl>(C->getCapturedVar())->getInitStyle() !=
+ VarDecl::CInit,
NewExprInit);
Result.Expansions.push_back(
InitCaptureInfoTy(NewExprInit, NewInitCaptureType));
@@ -13147,7 +13148,7 @@
if (E->isInitCapture(C)) {
TransformedInitCapture &NewC = InitCaptures[C - E->capture_begin()];
- VarDecl *OldVD = C->getCapturedVar();
+ VarDecl *OldVD = cast<VarDecl>(C->getCapturedVar());
llvm::SmallVector<Decl*, 4> NewVDs;
for (InitCaptureInfoTy &Info : NewC.Expansions) {
@@ -13202,7 +13203,7 @@
// The transform has determined that we should perform an expansion;
// transform and capture each of the arguments.
// expansion of the pattern. Do so.
- VarDecl *Pack = C->getCapturedVar();
+ VarDecl *Pack = cast<VarDecl>(C->getCapturedVar());
for (unsigned I = 0; I != *NumExpansions; ++I) {
Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(getSema(), I);
VarDecl *CapturedVar
Index: clang/lib/Sema/SemaStmt.cpp
===================================================================
--- clang/lib/Sema/SemaStmt.cpp
+++ clang/lib/Sema/SemaStmt.cpp
@@ -4688,11 +4688,11 @@
if (S.getLangOpts().OpenMP && RSI->CapRegionKind == CR_OpenMP)
S.setOpenMPCaptureKind(Field, Cap.getVariable(), RSI->OpenMPLevel);
- Captures.push_back(CapturedStmt::Capture(Cap.getLocation(),
- Cap.isReferenceCapture()
- ? CapturedStmt::VCK_ByRef
- : CapturedStmt::VCK_ByCopy,
- Cap.getVariable()));
+ Captures.push_back(CapturedStmt::Capture(
+ Cap.getLocation(),
+ Cap.isReferenceCapture() ? CapturedStmt::VCK_ByRef
+ : CapturedStmt::VCK_ByCopy,
+ cast<VarDecl>(Cap.getVariable())));
}
CaptureInits.push_back(Init.get());
}
Index: clang/lib/Sema/SemaOpenMP.cpp
===================================================================
--- clang/lib/Sema/SemaOpenMP.cpp
+++ clang/lib/Sema/SemaOpenMP.cpp
@@ -4524,12 +4524,12 @@
DSAStack->setForceCaptureByReferenceInTargetExecutable(
/*V=*/true);
if (RD->isLambda()) {
- llvm::DenseMap<const VarDecl *, FieldDecl *> Captures;
+ llvm::DenseMap<const ValueDecl *, FieldDecl *> Captures;
FieldDecl *ThisCapture;
RD->getCaptureFields(Captures, ThisCapture);
for (const LambdaCapture &LC : RD->captures()) {
if (LC.getCaptureKind() == LCK_ByRef) {
- VarDecl *VD = LC.getCapturedVar();
+ VarDecl *VD = cast<VarDecl>(LC.getCapturedVar());
DeclContext *VDC = VD->getDeclContext();
if (!VDC->Encloses(CurContext))
continue;
Index: clang/lib/Sema/SemaLambda.cpp
===================================================================
--- clang/lib/Sema/SemaLambda.cpp
+++ clang/lib/Sema/SemaLambda.cpp
@@ -1088,7 +1088,7 @@
if (C->Init.isInvalid())
continue;
- VarDecl *Var = nullptr;
+ ValueDecl *Var = nullptr;
if (C->Init.isUsable()) {
Diag(C->Loc, getLangOpts().CPlusPlus14
? diag::warn_cxx11_compat_init_capture
@@ -1166,7 +1166,10 @@
continue;
}
- Var = R.getAsSingle<VarDecl>();
+ if (auto BD = R.getAsSingle<BindingDecl>())
+ Var = BD;
+ else
+ Var = R.getAsSingle<VarDecl>();
if (Var && DiagnoseUseOfDecl(Var, C->Loc))
continue;
}
@@ -1200,7 +1203,14 @@
if (Var->isInvalidDecl())
continue;
- if (!Var->hasLocalStorage()) {
+ VarDecl *Underlying;
+ if (isa<BindingDecl>(Var)) {
+ Underlying = dyn_cast_or_null<VarDecl>(
+ cast<BindingDecl>(Var)->getDecomposedDecl());
+ } else
+ Underlying = cast<VarDecl>(Var);
+
+ if (!Underlying->hasLocalStorage()) {
Diag(C->Loc, diag::err_capture_non_automatic_variable) << C->Id;
Diag(Var->getLocation(), diag::note_previous_decl) << C->Id;
continue;
@@ -1224,7 +1234,7 @@
}
if (C->Init.isUsable()) {
- addInitCapture(LSI, Var);
+ addInitCapture(LSI, cast<VarDecl>(Var));
} else {
TryCaptureKind Kind = C->Kind == LCK_ByRef ? TryCapture_ExplicitByRef :
TryCapture_ExplicitByVal;
@@ -1574,7 +1584,7 @@
// An init-capture is initialized directly from its stored initializer.
if (Cap.isInitCapture())
- return Cap.getVariable()->getInit();
+ return cast<VarDecl>(Cap.getVariable())->getInit();
// For anything else, build an initialization expression. For an implicit
// capture, the capture notionally happens at the capture-default, so use
@@ -1605,7 +1615,7 @@
Init = This;
} else {
assert(Cap.isVariableCapture() && "unknown kind of capture");
- VarDecl *Var = Cap.getVariable();
+ ValueDecl *Var = Cap.getVariable();
Name = Var->getIdentifier();
Init = BuildDeclarationNameExpr(
CXXScopeSpec(), DeclarationNameInfo(Var->getDeclName(), Loc), Var);
@@ -1654,7 +1664,7 @@
bool Sema::CaptureHasSideEffects(const Capture &From) {
if (From.isInitCapture()) {
- Expr *Init = From.getVariable()->getInit();
+ Expr *Init = cast<VarDecl>(From.getVariable())->getInit();
if (Init && Init->HasSideEffects(Context))
return true;
}
@@ -1704,9 +1714,9 @@
TypeSourceInfo *TSI = nullptr;
if (Capture.isVariableCapture()) {
- auto *Var = Capture.getVariable();
- if (Var->isInitCapture())
- TSI = Capture.getVariable()->getTypeSourceInfo();
+ VarDecl *Var = llvm::dyn_cast_or_null<VarDecl>(Capture.getVariable());
+ if (Var && Var->isInitCapture())
+ TSI = Var->getTypeSourceInfo();
}
// FIXME: Should we really be doing this? A null TypeSourceInfo seems more
@@ -1854,7 +1864,7 @@
return LambdaCapture(From.getLocation(), IsImplicit, LCK_VLAType);
} else {
assert(From.isVariableCapture() && "unknown kind of capture");
- VarDecl *Var = From.getVariable();
+ ValueDecl *Var = From.getVariable();
LambdaCaptureKind Kind =
From.isCopyCapture() ? LCK_ByCopy : LCK_ByRef;
return LambdaCapture(From.getLocation(), IsImplicit, Kind, Var,
Index: clang/lib/Sema/SemaInit.cpp
===================================================================
--- clang/lib/Sema/SemaInit.cpp
+++ clang/lib/Sema/SemaInit.cpp
@@ -7846,9 +7846,11 @@
break;
// FIXME: We can't easily tell apart an init-capture from a nested
// capture of an init-capture.
- const VarDecl *VD = Elem.Capture->getCapturedVar();
+ const ValueDecl *VD = Elem.Capture->getCapturedVar();
+ bool InitCapture =
+ isa<VarDecl>(VD) && cast<VarDecl>(VD)->isInitCapture();
Diag(Elem.Capture->getLocation(), diag::note_lambda_capture_initializer)
- << VD << VD->isInitCapture() << Elem.Capture->isExplicit()
+ << VD << InitCapture << Elem.Capture->isExplicit()
<< (Elem.Capture->getCaptureKind() == LCK_ByRef) << VD
<< nextPathEntryRange(Path, I + 1, L);
break;
Index: clang/lib/Sema/SemaExpr.cpp
===================================================================
--- clang/lib/Sema/SemaExpr.cpp
+++ clang/lib/Sema/SemaExpr.cpp
@@ -2082,9 +2082,8 @@
NestedNameSpecifierLoc NNS, NamedDecl *FoundD,
SourceLocation TemplateKWLoc,
const TemplateArgumentListInfo *TemplateArgs) {
- bool RefersToCapturedVariable =
- isa<VarDecl>(D) &&
- NeedToCaptureVariable(cast<VarDecl>(D), NameInfo.getLoc());
+ bool RefersToCapturedVariable = isa<VarDecl, BindingDecl>(D) &&
+ NeedToCaptureVariable(D, NameInfo.getLoc());
DeclRefExpr *E = DeclRefExpr::Create(
Context, NNS, TemplateKWLoc, D, RefersToCapturedVariable, NameInfo, Ty,
@@ -3415,13 +3414,13 @@
// These are always lvalues.
valueKind = VK_LValue;
type = type.getNonReferenceType();
- // FIXME: Support lambda-capture of BindingDecls, once CWG actually
- // decides how that's supposed to work.
auto *BD = cast<BindingDecl>(VD);
- if (BD->getDeclContext() != CurContext) {
- auto *DD = dyn_cast_or_null<VarDecl>(BD->getDecomposedDecl());
- if (DD && DD->hasLocalStorage())
- diagnoseUncapturableValueReference(*this, Loc, BD);
+ auto *DD = dyn_cast_or_null<VarDecl>(BD->getDecomposedDecl());
+ if (DD && DD->hasLocalStorage()) {
+ if ((!LangOpts.CPlusPlus || !isLambdaCallOperator(CurContext)) &&
+ BD->getDeclContext() != CurContext) {
+ diagnoseUncapturableValueReference(*this, Loc, VD);
+ }
}
break;
}
@@ -16356,7 +16355,7 @@
if (Cap.isInvalid() || Cap.isThisCapture())
continue;
- VarDecl *Var = Cap.getVariable();
+ VarDecl *Var = cast<VarDecl>(Cap.getVariable());
Expr *CopyExpr = nullptr;
if (getLangOpts().CPlusPlus && Cap.isCopyCapture()) {
if (const RecordType *Record =
@@ -18187,8 +18186,8 @@
MarkVarDeclODRUsed(Capture, Loc, *this, &CapturingScopeIndex);
}
-static void diagnoseUncapturableValueReference(Sema &S, SourceLocation loc,
- ValueDecl *var) {
+void diagnoseUncapturableValueReference(Sema &S, SourceLocation loc,
+ ValueDecl *var) {
DeclContext *VarDC = var->getDeclContext();
// If the parameter still belongs to the translation unit, then
@@ -18228,12 +18227,12 @@
// capture.
}
-
-static bool isVariableAlreadyCapturedInScopeInfo(CapturingScopeInfo *CSI, VarDecl *Var,
- bool &SubCapturesAreNested,
- QualType &CaptureType,
- QualType &DeclRefType) {
- // Check whether we've already captured it.
+static bool isVariableAlreadyCapturedInScopeInfo(CapturingScopeInfo *CSI,
+ ValueDecl *Var,
+ bool &SubCapturesAreNested,
+ QualType &CaptureType,
+ QualType &DeclRefType) {
+ // Check whether we've already captured it.
if (CSI->CaptureMap.count(Var)) {
// If we found a capture, any subcaptures are nested.
SubCapturesAreNested = true;
@@ -18260,14 +18259,16 @@
// Only block literals, captured statements, and lambda expressions can
// capture; other scopes don't work.
-static DeclContext *getParentOfCapturingContextOrNull(DeclContext *DC, VarDecl *Var,
- SourceLocation Loc,
- const bool Diagnose, Sema &S) {
+static DeclContext *getParentOfCapturingContextOrNull(DeclContext *DC,
+ ValueDecl *Var,
+ SourceLocation Loc,
+ const bool Diagnose,
+ Sema &S) {
if (isa<BlockDecl>(DC) || isa<CapturedDecl>(DC) || isLambdaCallOperator(DC))
return getLambdaAwareParentOfDeclContext(DC);
- else if (Var->hasLocalStorage()) {
- if (Diagnose)
- diagnoseUncapturableValueReference(S, Loc, Var);
+ else if (VarDecl *VD = dyn_cast<VarDecl>(Var)) {
+ if (VD->hasLocalStorage() && Diagnose)
+ diagnoseUncapturableValueReference(S, Loc, VD);
}
return nullptr;
}
@@ -18275,9 +18276,12 @@
// Certain capturing entities (lambdas, blocks etc.) are not allowed to capture
// certain types of variables (unnamed, variably modified types etc.)
// so check for eligibility.
-static bool isVariableCapturable(CapturingScopeInfo *CSI, VarDecl *Var,
- SourceLocation Loc,
- const bool Diagnose, Sema &S) {
+static bool isVariableCapturable(CapturingScopeInfo *CSI, ValueDecl *Var,
+ SourceLocation Loc, const bool Diagnose,
+ Sema &S) {
+
+ assert((isa<VarDecl>(Var) || isa<BindingDecl>(Var)) &&
+ "Only variables and structured bindings can be captured");
bool IsBlock = isa<BlockScopeInfo>(CSI);
bool IsLambda = isa<LambdaScopeInfo>(CSI);
@@ -18334,17 +18338,26 @@
return false;
}
+ if (isa<BindingDecl>(Var)) {
+ if (!IsLambda || !S.getLangOpts().CPlusPlus) {
+ return false;
+ } else if (Diagnose && S.getLangOpts().CPlusPlus) {
+ S.Diag(Loc, S.LangOpts.CPlusPlus20
+ ? diag::warn_cxx17_compat_capture_binding
+ : diag::ext_capture_binding)
+ << Var;
+ S.Diag(Var->getLocation(), diag::note_entity_declared_at) << Var;
+ }
+ }
+
return true;
}
// Returns true if the capture by block was successful.
-static bool captureInBlock(BlockScopeInfo *BSI, VarDecl *Var,
- SourceLocation Loc,
- const bool BuildAndDiagnose,
- QualType &CaptureType,
- QualType &DeclRefType,
- const bool Nested,
- Sema &S, bool Invalid) {
+static bool captureInBlock(BlockScopeInfo *BSI, ValueDecl *Var,
+ SourceLocation Loc, const bool BuildAndDiagnose,
+ QualType &CaptureType, QualType &DeclRefType,
+ const bool Nested, Sema &S, bool Invalid) {
bool ByRef = false;
// Blocks are not allowed to capture arrays, excepting OpenCL.
@@ -18408,10 +18421,9 @@
return !Invalid;
}
-
/// Capture the given variable in the captured region.
static bool captureInCapturedRegion(
- CapturedRegionScopeInfo *RSI, VarDecl *Var, SourceLocation Loc,
+ CapturedRegionScopeInfo *RSI, ValueDecl *Var, SourceLocation Loc,
const bool BuildAndDiagnose, QualType &CaptureType, QualType &DeclRefType,
const bool RefersToCapturedVariable, Sema::TryCaptureKind Kind,
bool IsTopScope, Sema &S, bool Invalid) {
@@ -18450,16 +18462,12 @@
}
/// Capture the given variable in the lambda.
-static bool captureInLambda(LambdaScopeInfo *LSI,
- VarDecl *Var,
- SourceLocation Loc,
- const bool BuildAndDiagnose,
- QualType &CaptureType,
- QualType &DeclRefType,
+static bool captureInLambda(LambdaScopeInfo *LSI, ValueDecl *Var,
+ SourceLocation Loc, const bool BuildAndDiagnose,
+ QualType &CaptureType, QualType &DeclRefType,
const bool RefersToCapturedVariable,
const Sema::TryCaptureKind Kind,
- SourceLocation EllipsisLoc,
- const bool IsTopScope,
+ SourceLocation EllipsisLoc, const bool IsTopScope,
Sema &S, bool Invalid) {
// Determine whether we are capturing by reference or by value.
bool ByRef = false;
@@ -18468,6 +18476,43 @@
} else {
ByRef = (LSI->ImpCaptureStyle == LambdaScopeInfo::ImpCap_LambdaByref);
}
+ // C++20 : [expr.prim.lambda.capture]p12
+ // A bit-field or a member of an anonymous union shall
+ // not be captured by reference.
+ MemberExpr *ME = nullptr;
+ BindingDecl *BD = nullptr;
+ if (VarDecl *V = dyn_cast_or_null<VarDecl>(Var)) {
+ if (V->getInit())
+ ME = dyn_cast<MemberExpr>(V->getInit()->IgnoreImplicit());
+ } else if ((BD = dyn_cast<BindingDecl>(Var))) {
+ ME = dyn_cast_or_null<MemberExpr>(BD->getBinding());
+ }
+
+ // Capturing a bitfield by reference is not allowed
+ // except in OpenMP mode
+ if (ByRef && ME &&
+ (isa<BindingDecl>(Var) || !S.LangOpts.OpenMP ||
+ !S.isOpenMPCapturedDecl(Var))) {
+ FieldDecl *FD = dyn_cast_or_null<FieldDecl>(ME->getMemberDecl());
+ if (FD &&
+ (FD->isBitField() || (FD->getParent()->isAnonymousStructOrUnion() &&
+ FD->getParent()->isUnion()))) {
+ if (BuildAndDiagnose) {
+ S.Diag(Loc, diag::err_bitfield_capture_by_ref) << Var;
+ S.Diag(Var->getLocation(), diag::note_entity_declared_at) << Var;
+ S.Diag(FD->getLocation(), diag::note_bitfield_decl) << FD;
+ }
+ Invalid = true;
+ }
+ }
+ // FIXME: We should support capturing structured bindings in OpenMP
+ if (!Invalid && BD && S.LangOpts.OpenMP) {
+ if (BuildAndDiagnose) {
+ S.Diag(Loc, diag::err_capture_binding_openmp) << Var;
+ S.Diag(Var->getLocation(), diag::note_entity_declared_at) << Var;
+ }
+ Invalid = true;
+ }
// Compute the type of the field that will capture this variable.
if (ByRef) {
@@ -18549,7 +18594,8 @@
return !Invalid;
}
-static bool canCaptureVariableByCopy(VarDecl *Var, const ASTContext &Context) {
+static bool canCaptureVariableByCopy(ValueDecl *Var,
+ const ASTContext &Context) {
// Offer a Copy fix even if the type is dependent.
if (Var->getType()->isDependentType())
return true;
@@ -18575,7 +18621,7 @@
/// standard, for example we can't emit a default copy capture fix-it if we
/// already explicitly copy capture capture another variable.
static void buildLambdaCaptureFixit(Sema &Sema, LambdaScopeInfo *LSI,
- VarDecl *Var) {
+ ValueDecl *Var) {
assert(LSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_None);
// Don't offer Capture by copy of default capture by copy fixes if Var is
// known not to be copy constructible.
@@ -18651,14 +18697,21 @@
}
bool Sema::tryCaptureVariable(
- VarDecl *Var, SourceLocation ExprLoc, TryCaptureKind Kind,
+ ValueDecl *Var, SourceLocation ExprLoc, TryCaptureKind Kind,
SourceLocation EllipsisLoc, bool BuildAndDiagnose, QualType &CaptureType,
QualType &DeclRefType, const unsigned *const FunctionScopeIndexToStopAt) {
// An init-capture is notionally from the context surrounding its
// declaration, but its parent DC is the lambda class.
DeclContext *VarDC = Var->getDeclContext();
- if (Var->isInitCapture())
- VarDC = VarDC->getParent();
+ VarDecl *VD = dyn_cast<VarDecl>(Var);
+ if (VD) {
+ if (VD && VD->isInitCapture())
+ VarDC = VarDC->getParent();
+ } else {
+ VD = dyn_cast<DecompositionDecl>(
+ cast<BindingDecl>(Var)->getDecomposedDecl());
+ }
+ assert(VD && "Cannot capture a null variable");
DeclContext *DC = CurContext;
const unsigned MaxFunctionScopesIndex = FunctionScopeIndexToStopAt
@@ -18680,12 +18733,14 @@
// Capture global variables if it is required to use private copy of this
// variable.
- bool IsGlobal = !Var->hasLocalStorage();
+ bool IsGlobal = !VD->hasLocalStorage();
if (IsGlobal &&
!(LangOpts.OpenMP && isOpenMPCapturedDecl(Var, /*CheckScopeInfo=*/true,
MaxFunctionScopesIndex)))
return true;
- Var = Var->getCanonicalDecl();
+
+ if (isa<VarDecl>(Var))
+ Var = cast<VarDecl>(Var->getCanonicalDecl());
// Walk up the stack to determine whether we can capture the variable,
// performing the "simple" checks that don't depend on type. We stop when
@@ -18889,7 +18944,7 @@
return Invalid;
}
-bool Sema::tryCaptureVariable(VarDecl *Var, SourceLocation Loc,
+bool Sema::tryCaptureVariable(ValueDecl *Var, SourceLocation Loc,
TryCaptureKind Kind, SourceLocation EllipsisLoc) {
QualType CaptureType;
QualType DeclRefType;
@@ -18898,7 +18953,7 @@
DeclRefType, nullptr);
}
-bool Sema::NeedToCaptureVariable(VarDecl *Var, SourceLocation Loc) {
+bool Sema::NeedToCaptureVariable(ValueDecl *Var, SourceLocation Loc) {
QualType CaptureType;
QualType DeclRefType;
return !tryCaptureVariable(Var, Loc, TryCapture_Implicit, SourceLocation(),
@@ -18906,7 +18961,7 @@
DeclRefType, nullptr);
}
-QualType Sema::getCapturedDeclRefType(VarDecl *Var, SourceLocation Loc) {
+QualType Sema::getCapturedDeclRefType(ValueDecl *Var, SourceLocation Loc) {
QualType CaptureType;
QualType DeclRefType;
@@ -19502,6 +19557,24 @@
}
}
+static void DoMarkBindingDeclReferenced(Sema &SemaRef, SourceLocation Loc,
+ BindingDecl *BD) {
+ BD->setReferenced();
+
+ if (BD->isInvalidDecl())
+ return;
+
+ OdrUseContext OdrUse = isOdrUseContext(SemaRef);
+ if (OdrUse == OdrUseContext::Used) {
+ QualType CaptureType, DeclRefType;
+ SemaRef.tryCaptureVariable(BD, Loc, Sema::TryCapture_Implicit,
+ /*EllipsisLoc*/ SourceLocation(),
+ /*BuildAndDiagnose*/ true, CaptureType,
+ DeclRefType,
+ /*FunctionScopeIndexToStopAt*/ nullptr);
+ }
+}
+
/// Mark a variable referenced, and check whether it is odr-used
/// (C++ [basic.def.odr]p2, C99 6.9p3). Note that this should not be
/// used directly for normal expressions referring to VarDecl.
@@ -19521,6 +19594,11 @@
return;
}
+ if (BindingDecl *Decl = dyn_cast<BindingDecl>(D)) {
+ DoMarkBindingDeclReferenced(SemaRef, Loc, Decl);
+ return;
+ }
+
SemaRef.MarkAnyDeclReferenced(Loc, D, MightBeOdrUse);
// If this is a call to a method via a cast, also mark the method in the
Index: clang/lib/Sema/SemaDecl.cpp
===================================================================
--- clang/lib/Sema/SemaDecl.cpp
+++ clang/lib/Sema/SemaDecl.cpp
@@ -14526,9 +14526,11 @@
auto I = LambdaClass->field_begin();
for (const auto &C : LambdaClass->captures()) {
if (C.capturesVariable()) {
- VarDecl *VD = C.getCapturedVar();
- if (VD->isInitCapture())
- S.CurrentInstantiationScope->InstantiatedLocal(VD, VD);
+ ValueDecl *VD = C.getCapturedVar();
+ if (VarDecl *Var = dyn_cast<VarDecl>(VD)) {
+ if (Var->isInitCapture())
+ S.CurrentInstantiationScope->InstantiatedLocal(VD, VD);
+ }
const bool ByRef = C.getCaptureKind() == LCK_ByRef;
LSI->addCapture(VD, /*IsBlock*/false, ByRef,
/*RefersToEnclosingVariableOrCapture*/true, C.getLocation(),
Index: clang/lib/Sema/ScopeInfo.cpp
===================================================================
--- clang/lib/Sema/ScopeInfo.cpp
+++ clang/lib/Sema/ScopeInfo.cpp
@@ -220,7 +220,8 @@
bool Capture::isInitCapture() const {
// Note that a nested capture of an init-capture is not itself an
// init-capture.
- return !isNested() && isVariableCapture() && getVariable()->isInitCapture();
+ return !isNested() && isVariableCapture() && isa<VarDecl>(getVariable()) &&
+ cast<VarDecl>(getVariable())->isInitCapture();
}
bool CapturingScopeInfo::isVLATypeCaptured(const VariableArrayType *VAT) const {
Index: clang/lib/CodeGen/CodeGenFunction.h
===================================================================
--- clang/lib/CodeGen/CodeGenFunction.h
+++ clang/lib/CodeGen/CodeGenFunction.h
@@ -609,7 +609,7 @@
const CodeGen::CGBlockInfo *BlockInfo = nullptr;
llvm::Value *BlockPointer = nullptr;
- llvm::DenseMap<const VarDecl *, FieldDecl *> LambdaCaptureFields;
+ llvm::DenseMap<const ValueDecl *, FieldDecl *> LambdaCaptureFields;
FieldDecl *LambdaThisCaptureField = nullptr;
/// A mapping from NRVO variables to the flags used to indicate
Index: clang/lib/CodeGen/CGOpenMPRuntimeGPU.cpp
===================================================================
--- clang/lib/CodeGen/CGOpenMPRuntimeGPU.cpp
+++ clang/lib/CodeGen/CGOpenMPRuntimeGPU.cpp
@@ -3810,7 +3810,7 @@
else
VDLVal = CGF.MakeAddrLValue(
VDAddr, VD->getType().getCanonicalType().getNonReferenceType());
- llvm::DenseMap<const VarDecl *, FieldDecl *> Captures;
+ llvm::DenseMap<const ValueDecl *, FieldDecl *> Captures;
FieldDecl *ThisCapture = nullptr;
RD->getCaptureFields(Captures, ThisCapture);
if (ThisCapture && CGF.CapturedStmtInfo->isCXXThisExprCaptured()) {
@@ -3822,13 +3822,15 @@
for (const LambdaCapture &LC : RD->captures()) {
if (LC.getCaptureKind() != LCK_ByRef)
continue;
- const VarDecl *VD = LC.getCapturedVar();
- if (!CS->capturesVariable(VD))
+ const ValueDecl *VD = LC.getCapturedVar();
+ // FIXME: For now VD is always a VarDecl because OpenMP does not support
+ // capturing structured bindings in lambdas yet.
+ if (!CS->capturesVariable(cast<VarDecl>(VD)))
continue;
auto It = Captures.find(VD);
assert(It != Captures.end() && "Found lambda capture without field.");
LValue VarLVal = CGF.EmitLValueForFieldInitialization(VDLVal, It->second);
- Address VDAddr = CGF.GetAddrOfLocalVar(VD);
+ Address VDAddr = CGF.GetAddrOfLocalVar(cast<VarDecl>(VD));
if (VD->getType().getCanonicalType()->isReferenceType())
VDAddr = CGF.EmitLoadOfReferenceLValue(VDAddr,
VD->getType().getCanonicalType())
Index: clang/lib/CodeGen/CGOpenMPRuntime.cpp
===================================================================
--- clang/lib/CodeGen/CGOpenMPRuntime.cpp
+++ clang/lib/CodeGen/CGOpenMPRuntime.cpp
@@ -409,7 +409,7 @@
/// RAII for emitting code of OpenMP constructs.
class InlinedOpenMPRegionRAII {
CodeGenFunction &CGF;
- llvm::DenseMap<const VarDecl *, FieldDecl *> LambdaCaptureFields;
+ llvm::DenseMap<const ValueDecl *, FieldDecl *> LambdaCaptureFields;
FieldDecl *LambdaThisCaptureField = nullptr;
const CodeGen::CGBlockInfo *BlockInfo = nullptr;
bool NoInheritance = false;
@@ -8949,7 +8949,7 @@
Address VDAddr(Arg, CGF.ConvertTypeForMem(VDType),
CGF.getContext().getDeclAlign(VD));
LValue VDLVal = CGF.MakeAddrLValue(VDAddr, VDType);
- llvm::DenseMap<const VarDecl *, FieldDecl *> Captures;
+ llvm::DenseMap<const ValueDecl *, FieldDecl *> Captures;
FieldDecl *ThisCapture = nullptr;
RD->getCaptureFields(Captures, ThisCapture);
if (ThisCapture) {
@@ -8971,7 +8971,7 @@
for (const LambdaCapture &LC : RD->captures()) {
if (!LC.capturesVariable())
continue;
- const VarDecl *VD = LC.getCapturedVar();
+ const VarDecl *VD = cast<VarDecl>(LC.getCapturedVar());
if (LC.getCaptureKind() != LCK_ByRef && !VD->getType()->isPointerType())
continue;
auto It = Captures.find(VD);
Index: clang/lib/CodeGen/CGExpr.cpp
===================================================================
--- clang/lib/CodeGen/CGExpr.cpp
+++ clang/lib/CodeGen/CGExpr.cpp
@@ -2927,8 +2927,14 @@
// FIXME: While we're emitting a binding from an enclosing scope, all other
// DeclRefExprs we see should be implicitly treated as if they also refer to
// an enclosing scope.
- if (const auto *BD = dyn_cast<BindingDecl>(ND))
+ if (const auto *BD = dyn_cast<BindingDecl>(ND)) {
+ if (E->refersToEnclosingVariableOrCapture()) {
+ auto *FD = LambdaCaptureFields.lookup(BD);
+ assert(FD);
+ return EmitCapturedFieldLValue(*this, FD, CXXABIThisValue);
+ }
return EmitLValue(BD->getBinding());
+ }
// We can form DeclRefExprs naming GUID declarations when reconstituting
// non-type template parameters into expressions.
Index: clang/lib/CodeGen/CGDebugInfo.cpp
===================================================================
--- clang/lib/CodeGen/CGDebugInfo.cpp
+++ clang/lib/CodeGen/CGDebugInfo.cpp
@@ -1495,7 +1495,7 @@
if (C.capturesVariable()) {
SourceLocation Loc = C.getLocation();
assert(!Field->isBitField() && "lambdas don't have bitfield members!");
- VarDecl *V = C.getCapturedVar();
+ ValueDecl *V = C.getCapturedVar();
StringRef VName = V->getName();
llvm::DIFile *VUnit = getOrCreateFile(Loc);
auto Align = getDeclAlignIfRequired(V, CGM.getContext());
Index: clang/lib/Analysis/AnalysisDeclContext.cpp
===================================================================
--- clang/lib/Analysis/AnalysisDeclContext.cpp
+++ clang/lib/Analysis/AnalysisDeclContext.cpp
@@ -169,8 +169,8 @@
if (!LC.capturesVariable())
continue;
- VarDecl *VD = LC.getCapturedVar();
- if (isSelfDecl(VD))
+ ValueDecl *VD = LC.getCapturedVar();
+ if (isSelfDecl(dyn_cast<VarDecl>(VD)))
return dyn_cast<ImplicitParamDecl>(VD);
}
Index: clang/lib/AST/StmtPrinter.cpp
===================================================================
--- clang/lib/AST/StmtPrinter.cpp
+++ clang/lib/AST/StmtPrinter.cpp
@@ -2090,7 +2090,7 @@
OS << "...";
if (Node->isInitCapture(C)) {
- VarDecl *D = C->getCapturedVar();
+ VarDecl *D = cast<VarDecl>(C->getCapturedVar());
llvm::StringRef Pre;
llvm::StringRef Post;
Index: clang/lib/AST/ExprConstant.cpp
===================================================================
--- clang/lib/AST/ExprConstant.cpp
+++ clang/lib/AST/ExprConstant.cpp
@@ -578,7 +578,7 @@
/// LambdaCaptureFields - Mapping from captured variables/this to
/// corresponding data members in the closure class.
- llvm::DenseMap<const VarDecl *, FieldDecl *> LambdaCaptureFields;
+ llvm::DenseMap<const ValueDecl *, FieldDecl *> LambdaCaptureFields;
FieldDecl *LambdaThisCaptureField;
CallStackFrame(EvalInfo &Info, SourceLocation CallLoc,
Index: clang/lib/AST/ExprCXX.cpp
===================================================================
--- clang/lib/AST/ExprCXX.cpp
+++ clang/lib/AST/ExprCXX.cpp
@@ -1087,7 +1087,7 @@
: Expr(SC, Empty), NumArgs(NumArgs) {}
LambdaCapture::LambdaCapture(SourceLocation Loc, bool Implicit,
- LambdaCaptureKind Kind, VarDecl *Var,
+ LambdaCaptureKind Kind, ValueDecl *Var,
SourceLocation EllipsisLoc)
: DeclAndBits(Var, 0), Loc(Loc), EllipsisLoc(EllipsisLoc) {
unsigned Bits = 0;
@@ -1211,7 +1211,8 @@
}
bool LambdaExpr::isInitCapture(const LambdaCapture *C) const {
- return (C->capturesVariable() && C->getCapturedVar()->isInitCapture() &&
+ return (C->capturesVariable() && isa<VarDecl>(C->getCapturedVar()) &&
+ cast<VarDecl>(C->getCapturedVar())->isInitCapture() &&
(getCallOperator() == C->getCapturedVar()->getDeclContext()));
}
Index: clang/lib/AST/DeclCXX.cpp
===================================================================
--- clang/lib/AST/DeclCXX.cpp
+++ clang/lib/AST/DeclCXX.cpp
@@ -1570,8 +1570,8 @@
}
void CXXRecordDecl::getCaptureFields(
- llvm::DenseMap<const VarDecl *, FieldDecl *> &Captures,
- FieldDecl *&ThisCapture) const {
+ llvm::DenseMap<const ValueDecl *, FieldDecl *> &Captures,
+ FieldDecl *&ThisCapture) const {
Captures.clear();
ThisCapture = nullptr;
Index: clang/lib/AST/ASTImporter.cpp
===================================================================
--- clang/lib/AST/ASTImporter.cpp
+++ clang/lib/AST/ASTImporter.cpp
@@ -1005,7 +1005,7 @@
template <>
Expected<LambdaCapture> ASTNodeImporter::import(const LambdaCapture &From) {
- VarDecl *Var = nullptr;
+ ValueDecl *Var = nullptr;
if (From.capturesVariable()) {
if (auto VarOrErr = import(From.getCapturedVar()))
Var = *VarOrErr;
Index: clang/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ clang/include/clang/Sema/Sema.h
@@ -5307,23 +5307,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
Index: clang/include/clang/Sema/ScopeInfo.h
===================================================================
--- clang/include/clang/Sema/ScopeInfo.h
+++ clang/include/clang/Sema/ScopeInfo.h
@@ -548,7 +548,7 @@
const VariableArrayType *CapturedVLA;
/// Otherwise, the captured variable (if any).
- VarDecl *CapturedVar;
+ ValueDecl *CapturedVar;
};
/// The source location at which the first capture occurred.
@@ -584,12 +584,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) {}
@@ -634,7 +635,7 @@
NonODRUsed = true;
}
- VarDecl *getVariable() const {
+ ValueDecl *getVariable() const {
assert(isVariableCapture());
return CapturedVar;
}
@@ -673,7 +674,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.
@@ -690,7 +691,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,
@@ -717,23 +718,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];
}
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -9027,6 +9027,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">;
Index: clang/include/clang/ASTMatchers/ASTMatchers.h
===================================================================
--- clang/include/clang/ASTMatchers/ASTMatchers.h
+++ clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -4690,7 +4690,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);
Index: clang/include/clang/AST/Stmt.h
===================================================================
--- clang/include/clang/AST/Stmt.h
+++ clang/include/clang/AST/Stmt.h
@@ -59,6 +59,7 @@
class StringLiteral;
class Token;
class VarDecl;
+class ValueDecl;
//===----------------------------------------------------------------------===//
// AST classes for statements.
Index: clang/include/clang/AST/LambdaCapture.h
===================================================================
--- clang/include/clang/AST/LambdaCapture.h
+++ 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
Index: clang/include/clang/AST/DeclCXX.h
===================================================================
--- clang/include/clang/AST/DeclCXX.h
+++ 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>;
Index: clang/docs/ReleaseNotes.rst
===================================================================
--- clang/docs/ReleaseNotes.rst
+++ clang/docs/ReleaseNotes.rst
@@ -460,6 +460,9 @@
now possible to overload destructors using concepts. Note that the rest
of the paper about other special member functions is not yet implemented.
+- Support capturing structured bindings in lambdas
+ (`P1091R3 <https://wg21.link/p1091r3>`_ and `P1381R1 <https://wg21.link/P1381R1>`)
+
C++2b Feature Support
^^^^^^^^^^^^^^^^^^^^^
Index: clang-tools-extra/clang-tidy/modernize/LoopConvertUtils.cpp
===================================================================
--- clang-tools-extra/clang-tidy/modernize/LoopConvertUtils.cpp
+++ clang-tools-extra/clang-tidy/modernize/LoopConvertUtils.cpp
@@ -785,8 +785,8 @@
const LambdaCapture *C,
Expr *Init) {
if (C->capturesVariable()) {
- const VarDecl *VDecl = C->getCapturedVar();
- if (areSameVariable(IndexVar, cast<ValueDecl>(VDecl))) {
+ const ValueDecl *VDecl = C->getCapturedVar();
+ if (areSameVariable(IndexVar, VDecl)) {
// FIXME: if the index is captured, it will count as an usage and the
// alias (if any) won't work, because it is only used in case of having
// exactly one usage.
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits