]> granicus.if.org Git - clang/commitdiff
Permit static local structured bindings to be named from arbitrary scopes inside...
authorRichard Smith <richard-llvm@metafoo.co.uk>
Sat, 25 May 2019 01:04:17 +0000 (01:04 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Sat, 25 May 2019 01:04:17 +0000 (01:04 +0000)
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@361686 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/AST/DeclCXX.h
lib/AST/DeclCXX.cpp
lib/Sema/SemaExpr.cpp
lib/Serialization/ASTReaderDecl.cpp
test/CodeGenCXX/cxx1z-decomposition.cpp
test/SemaCXX/cxx1z-decomposition.cpp

index fc243de896eec891bf1116065a35327d1807d2b7..1f879c53f4776019518175ea0f698838984e4be7 100644 (file)
@@ -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<BindingDecl *>());
+    for (auto *B : Bindings)
+      B->setDecomposedDecl(this);
   }
 
   void anchor() override;
index f9f70ecb59041df61d670e20bd1035a6565da765..941fd66c7bab5033ffbf0b45c486faa611d05340 100644 (file)
@@ -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<ValueDecl>(Decomp.get(Source));
+}
+
 VarDecl *BindingDecl::getHoldingVar() const {
   Expr *B = getBinding();
   if (!B)
index 0c04f03f06d6d8612ce51e0f6f95beedbdecc164..3a12c2dd84ffba21f6eecedcbfc02b70e9b7ea83 100644 (file)
@@ -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<BindingDecl>(VD);
-      if (BD->getDeclContext()->isFunctionOrMethod() &&
-          BD->getDeclContext() != CurContext)
-        diagnoseUncapturableValueReference(*this, Loc, BD, CurContext);
+      if (BD->getDeclContext() != CurContext) {
+        auto *DD = dyn_cast_or_null<VarDecl>(BD->getDecomposedDecl());
+        if (DD && DD->hasLocalStorage())
+          diagnoseUncapturableValueReference(*this, Loc, BD, CurContext);
+      }
       break;
     }
 
index 27fcc9e40b51734d6daa12cc236c1b982c4171fd..5d40b85b03df75449fd32da1c294cc2b069e2775 100644 (file)
@@ -1459,8 +1459,10 @@ void ASTDeclReader::VisitParmVarDecl(ParmVarDecl *PD) {
 void ASTDeclReader::VisitDecompositionDecl(DecompositionDecl *DD) {
   VisitVarDecl(DD);
   auto **BDs = DD->getTrailingObjects<BindingDecl *>();
-  for (unsigned I = 0; I != DD->NumBindings; ++I)
+  for (unsigned I = 0; I != DD->NumBindings; ++I) {
     BDs[I] = ReadDeclAs<BindingDecl>();
+    BDs[I]->setDecomposedDecl(DD);
+  }
 }
 
 void ASTDeclReader::VisitBindingDecl(BindingDecl *BD) {
index 8e71b1230c4d5a037fe6038121ce8e3d8317e8c8..31ade6f5fdb282f3bcfbce600dffbdbf550bfd0b 100644 (file)
@@ -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();
 }
index f174c79e5957343c3f99b9e4041214ac05f2b9ff..d2dc939beb5df30b1cc61778cdc0ffaa7ccfe5e1 100644 (file)
@@ -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() {