From: Richard Smith Date: Sat, 25 May 2019 01:04:17 +0000 (+0000) Subject: Permit static local structured bindings to be named from arbitrary scopes inside... X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=962d736d3cb56511d5f2f0d6a0444115694b395f;p=clang Permit static local structured bindings to be named from arbitrary scopes inside their declaring scope. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@361686 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/DeclCXX.h b/include/clang/AST/DeclCXX.h index fc243de896..1f879c53f4 100644 --- a/include/clang/AST/DeclCXX.h +++ b/include/clang/AST/DeclCXX.h @@ -63,6 +63,7 @@ class CXXDestructorDecl; class CXXFinalOverriderMap; class CXXIndirectPrimaryBaseSet; class CXXMethodDecl; +class DecompositionDecl; class DiagnosticBuilder; class FriendDecl; class FunctionTemplateDecl; @@ -3918,6 +3919,8 @@ public: /// x[0], x[1], and x[2] respectively, where x is the implicit /// DecompositionDecl of type 'int (&)[3]'. class BindingDecl : public ValueDecl { + /// The declaration that this binding binds to part of. + LazyDeclPtr Decomp; /// The binding represented by this declaration. References to this /// declaration are effectively equivalent to this expression (except /// that it is only evaluated once at the point of declaration of the @@ -3941,6 +3944,10 @@ public: /// decomposition declaration, and when the initializer is type-dependent. Expr *getBinding() const { return Binding; } + /// Get the decomposition declaration that this binding represents a + /// decomposition of. + ValueDecl *getDecomposedDecl() const; + /// Get the variable (if any) that holds the value of evaluating the binding. /// Only present for user-defined bindings for tuple-like types. VarDecl *getHoldingVar() const; @@ -3953,6 +3960,9 @@ public: this->Binding = Binding; } + /// Set the decomposed variable for this BindingDecl. + void setDecomposedDecl(ValueDecl *Decomposed) { Decomp = Decomposed; } + static bool classof(const Decl *D) { return classofKind(D->getKind()); } static bool classofKind(Kind K) { return K == Decl::Binding; } }; @@ -3980,6 +3990,8 @@ class DecompositionDecl final NumBindings(Bindings.size()) { std::uninitialized_copy(Bindings.begin(), Bindings.end(), getTrailingObjects()); + for (auto *B : Bindings) + B->setDecomposedDecl(this); } void anchor() override; diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index f9f70ecb59..941fd66c7b 100644 --- a/lib/AST/DeclCXX.cpp +++ b/lib/AST/DeclCXX.cpp @@ -2929,6 +2929,12 @@ BindingDecl *BindingDecl::CreateDeserialized(ASTContext &C, unsigned ID) { return new (C, ID) BindingDecl(nullptr, SourceLocation(), nullptr); } +ValueDecl *BindingDecl::getDecomposedDecl() const { + ExternalASTSource *Source = + Decomp.isOffset() ? getASTContext().getExternalSource() : nullptr; + return cast_or_null(Decomp.get(Source)); +} + VarDecl *BindingDecl::getHoldingVar() const { Expr *B = getBinding(); if (!B) diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 0c04f03f06..3a12c2dd84 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -3059,9 +3059,11 @@ ExprResult Sema::BuildDeclarationNameExpr( // FIXME: Support lambda-capture of BindingDecls, once CWG actually // decides how that's supposed to work. auto *BD = cast(VD); - if (BD->getDeclContext()->isFunctionOrMethod() && - BD->getDeclContext() != CurContext) - diagnoseUncapturableValueReference(*this, Loc, BD, CurContext); + if (BD->getDeclContext() != CurContext) { + auto *DD = dyn_cast_or_null(BD->getDecomposedDecl()); + if (DD && DD->hasLocalStorage()) + diagnoseUncapturableValueReference(*this, Loc, BD, CurContext); + } break; } diff --git a/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp index 27fcc9e40b..5d40b85b03 100644 --- a/lib/Serialization/ASTReaderDecl.cpp +++ b/lib/Serialization/ASTReaderDecl.cpp @@ -1459,8 +1459,10 @@ void ASTDeclReader::VisitParmVarDecl(ParmVarDecl *PD) { void ASTDeclReader::VisitDecompositionDecl(DecompositionDecl *DD) { VisitVarDecl(DD); auto **BDs = DD->getTrailingObjects(); - for (unsigned I = 0; I != DD->NumBindings; ++I) + for (unsigned I = 0; I != DD->NumBindings; ++I) { BDs[I] = ReadDeclAs(); + BDs[I]->setDecomposedDecl(DD); + } } void ASTDeclReader::VisitBindingDecl(BindingDecl *BD) { diff --git a/test/CodeGenCXX/cxx1z-decomposition.cpp b/test/CodeGenCXX/cxx1z-decomposition.cpp index 8e71b1230c..31ade6f5fd 100644 --- a/test/CodeGenCXX/cxx1z-decomposition.cpp +++ b/test/CodeGenCXX/cxx1z-decomposition.cpp @@ -129,7 +129,7 @@ void test_static_simple() { } // CHECK-LABEL: define {{.*}}@_Z17test_static_tuple -void test_static_tuple() { +int test_static_tuple() { // Note that the desugaring specified for this construct requires three // separate guarded initializations. It is possible for an exception to be // thrown after the first initialization and before the second, and if that @@ -162,4 +162,14 @@ void test_static_tuple() { // CHECK: store {{.*}}, {{.*}} @_ZGRZ17test_static_tuplevE2x2_ // CHECK: store {{.*}} @_ZGRZ17test_static_tuplevE2x2_, {{.*}} @_ZZ17test_static_tuplevE2x2 // CHECK: call void @__cxa_guard_release({{.*}} @_ZGVZ17test_static_tuplevE2x2) + + struct Inner { + // CHECK-LABEL: define {{.*}}@_ZZ17test_static_tuplevEN5Inner1fEv( + // FIXME: This first load should be constant-folded to the _ZGV... temporary. + // CHECK: load {{.*}} @_ZZ17test_static_tuplevE2x2 + // CHECK: load + // CHECK: ret + int f() { return x2; } + }; + return Inner().f(); } diff --git a/test/SemaCXX/cxx1z-decomposition.cpp b/test/SemaCXX/cxx1z-decomposition.cpp index f174c79e59..d2dc939beb 100644 --- a/test/SemaCXX/cxx1z-decomposition.cpp +++ b/test/SemaCXX/cxx1z-decomposition.cpp @@ -37,14 +37,19 @@ constexpr bool g(S &&s) { } static_assert(g({1, 2})); +auto [outer1, outer2] = S{1, 2}; void enclosing() { - struct S { int a; }; + struct S { int a = outer1; }; auto [n] = S(); // expected-note 2{{'n' declared here}} struct Q { int f() { return n; } }; // expected-error {{reference to local binding 'n' declared in enclosing function}} - // FIXME: This is probably supposed to be valid, but we do not have clear rules on how it's supposed to work. (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-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 bitfield() {