cor3ntin updated this revision to Diff 423228. cor3ntin added a comment. Fix several conflicrs with the recently committed change to lambda captures (D119136 <https://reviews.llvm.org/D119136>).
Also add to rewrite the code gen test. I'm not sure what, if anything changed there, and the generated IR *seems* fine? 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 @@ -3355,9 +3355,11 @@ C != CEnd; ++C) { if (!C->capturesVariable()) continue; - - if (Visit(MakeCursorVariableRef(C->getCapturedVar(), C->getLocation(), - TU))) + // TODO: hamdle 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,46 @@ +// 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 &>); + }; + } +} 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,41 @@ +// 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: %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 @@ -2553,7 +2553,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 @@ -5790,7 +5790,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 @@ -12904,7 +12904,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) { @@ -12921,7 +12921,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)); @@ -13088,7 +13089,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) { @@ -13144,7 +13145,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 @@ -4689,11 +4689,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 @@ -4485,12 +4485,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 @@ -1040,7 +1040,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 @@ -1076,7 +1076,7 @@ C->EllipsisLoc, C->Id, InitStyle, C->Init.get(), Method); assert(Var && "createLambdaInitCaptureVarDecl returned a null VarDecl?"); - CheckShadow(CurrentScope, Var); + CheckShadow(CurrentScope, cast<VarDecl>(Var)); PushOnScopeChains(Var, CurrentScope, false); } else { assert(C->InitKind == LambdaCaptureInitKind::NoInit && @@ -1115,7 +1115,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; } @@ -1152,7 +1155,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; @@ -1229,12 +1239,12 @@ S.CheckCXXThisCapture(C->Loc, true, true, nullptr, C->Kind == LCK_StarThis); } else { - VarDecl *Var = + ValueDecl *Var = LSI->DelayedCaptures[std::distance(Intro.Captures.begin(), C)].Var; if (!Var) continue; - if (Var->isInitCapture() && C->Init.isUsable()) { - S.addInitCapture(LSI, Var); + if (llvm::isa<VarDecl>(Var) && cast<VarDecl>(Var)->isInitCapture() && C->Init.isUsable()) { + S.addInitCapture(LSI, cast<VarDecl>(Var)); S.PushOnScopeChains(Var, S.getCurScope(), false); } else { Sema::TryCaptureKind Kind = C->Kind == LCK_ByRef @@ -1393,7 +1403,7 @@ ManglingContext.pop(); for (auto &&C : LSI->DelayedCaptures) { - VarDecl *Var = C.second.Var; + auto *Var = llvm::dyn_cast_or_null<VarDecl>(C.second.Var); if (Var && Var->isInitCapture()) { PushOnScopeChains(Var, CurScope, false); } @@ -1754,7 +1764,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 @@ -1785,7 +1795,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); @@ -1834,7 +1844,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; } @@ -1884,9 +1894,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 @@ -2034,7 +2044,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 @@ -7783,9 +7783,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 @@ -2050,9 +2050,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, @@ -3382,13 +3381,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 && !isUnevaluatedContext()) { - 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 && !isUnevaluatedContext()) { + diagnoseUncapturableValueReference(*this, Loc, VD); + } } break; } @@ -16250,7 +16249,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 = @@ -18079,8 +18078,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 @@ -18120,12 +18119,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; @@ -18152,14 +18151,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; } @@ -18167,9 +18168,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); @@ -18226,17 +18230,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. @@ -18300,10 +18313,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) { @@ -18342,16 +18354,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; @@ -18360,6 +18368,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) { @@ -18441,7 +18486,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; @@ -18467,7 +18513,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. @@ -18542,7 +18588,7 @@ } } -static bool CheckCaptureUseBeforeLambdaQualifiers(Sema &S, VarDecl *Var, +static bool CheckCaptureUseBeforeLambdaQualifiers(Sema &S, ValueDecl *Var, SourceLocation ExprLoc, LambdaScopeInfo *LSI) { if (Var->isInvalidDecl()) @@ -18552,7 +18598,7 @@ SourceLocation Loc = LSI->IntroducerRange.getBegin(); bool Explicitly = false; for (auto &&C : LSI->DelayedCaptures) { - VarDecl *CV = C.second.Var; + ValueDecl *CV = C.second.Var; if (Var != CV) continue; ByCopy = C.second.Kind == LambdaCaptureKind::LCK_ByCopy; @@ -18565,7 +18611,7 @@ // even when BuildAndDiagnose is false S.Diag(ExprLoc, diag::err_lambda_used_before_capture) << Var; S.Diag(Loc, diag::note_var_explicitly_captured_here) << Var << Explicitly; - if (!Var->isInitCapture()) + if (!isa<VarDecl>(Var) || !cast<VarDecl>(Var)->isInitCapture()) S.Diag(Var->getBeginLoc(), diag::note_entity_declared_at) << Var; Var->setInvalidDecl(); return false; @@ -18574,14 +18620,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 @@ -18598,12 +18651,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 @@ -18835,7 +18890,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; @@ -18844,7 +18899,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(), @@ -18852,7 +18907,7 @@ DeclRefType, nullptr); } -QualType Sema::getCapturedDeclRefType(VarDecl *Var, SourceLocation Loc) { +QualType Sema::getCapturedDeclRefType(ValueDecl *Var, SourceLocation Loc) { QualType CaptureType; QualType DeclRefType; @@ -19448,6 +19503,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. @@ -19467,6 +19540,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 @@ -14481,9 +14481,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 @@ -3809,7 +3809,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()) { @@ -3821,13 +3821,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; @@ -9035,7 +9035,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) { @@ -9057,7 +9057,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 @@ -2914,8 +2914,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 @@ -1493,7 +1493,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 @@ -2072,7 +2072,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 @@ -1556,8 +1556,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 @@ -5196,23 +5196,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]; } @@ -832,7 +831,7 @@ CXXMethodDecl *CallOperator = nullptr; struct DelayedCapture { - VarDecl *Var; + ValueDecl *Var; SourceLocation Loc; LambdaCaptureKind Kind; }; Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -8978,6 +8978,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 @@ -108,7 +108,7 @@ `51414 <https://github.com/llvm/llvm-project/issues/51414>`_, `51416 <https://github.com/llvm/llvm-project/issues/51416>`_, and `51641 <https://github.com/llvm/llvm-project/issues/51641>`_. -- The builtin function __builtin_dump_struct would crash clang when the target +- The builtin function __builtin_dump_struct would crash clang when the target struct contains a bitfield. It now correctly handles bitfields. This fixes Issue `Issue 54462 <https://github.com/llvm/llvm-project/issues/54462>`_. - Statement expressions are now disabled in default arguments in general. @@ -239,8 +239,8 @@ - No longer attempt to evaluate a consteval UDL function call at runtime when it is called through a template instantiation. This fixes `Issue 54578 <https://github.com/llvm/llvm-project/issues/54578>`_. - - Implemented `__builtin_source_location()` which enables library support for std::source_location. +- Support capturing structured bindings in lambdas (`P1091R3 <https://wg21.link/p1091r3>`_ and `P1381R1 <https://wg21.link/P1381R1>`) - The mangling scheme for C++20 modules has incompatibly changed. The initial mangling was discovered not to be reversible, and the weak 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 cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits