https://github.com/efriedma-quic updated 
https://github.com/llvm/llvm-project/pull/212368

>From d14791fd3043cad0d462bfad5386e113534b62b0 Mon Sep 17 00:00:00 2001
From: Eli Friedman <[email protected]>
Date: Mon, 27 Jul 2026 15:40:05 -0700
Subject: [PATCH 1/4] [clang] Compute value dependence for references to
 structured bindings

In some cases, a reference to a structured binding is value-dependent.
I think this has been possible since they were originally defined in
C++17. C++26 makes it easier to trigger issues, though.

The standard doesn't provide any explicit rules for what to do here, so
I invented a rule.  My invented rule seems to be consistent for the
cases I can come up with.

Fixes #211930
---
 clang/include/clang/AST/DeclCXX.h             |  6 ++--
 clang/lib/AST/ASTImporter.cpp                 |  3 +-
 clang/lib/AST/ComputeDependence.cpp           | 25 +++++++++++++
 .../test/SemaCXX/binding-value-dependence.cpp | 36 +++++++++++++++++++
 4 files changed, 66 insertions(+), 4 deletions(-)
 create mode 100644 clang/test/SemaCXX/binding-value-dependence.cpp

diff --git a/clang/include/clang/AST/DeclCXX.h 
b/clang/include/clang/AST/DeclCXX.h
index 45fb99e27b137..93c5d982e1d98 100644
--- a/clang/include/clang/AST/DeclCXX.h
+++ b/clang/include/clang/AST/DeclCXX.h
@@ -4205,7 +4205,7 @@ class StaticAssertDecl : public Decl {
 /// DecompositionDecl of type 'int (&)[3]'.
 class BindingDecl : public ValueDecl {
   /// The declaration that this binding binds to part of.
-  ValueDecl *Decomp = nullptr;
+  DecompositionDecl *Decomp = nullptr;
   /// 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
@@ -4236,7 +4236,7 @@ class BindingDecl : public ValueDecl {
 
   /// Get the decomposition declaration that this binding represents a
   /// decomposition of.
-  ValueDecl *getDecomposedDecl() const { return Decomp; }
+  DecompositionDecl *getDecomposedDecl() const { return Decomp; }
 
   /// Set the binding for this BindingDecl, along with its declared type (which
   /// should be a possibly-cv-qualified form of the type of the binding, or a
@@ -4247,7 +4247,7 @@ class BindingDecl : public ValueDecl {
   }
 
   /// Set the decomposed variable for this BindingDecl.
-  void setDecomposedDecl(ValueDecl *Decomposed) { Decomp = Decomposed; }
+  void setDecomposedDecl(DecompositionDecl *Decomposed) { Decomp = Decomposed; 
}
 
   /// Get the variable (if any) that holds the value of evaluating the binding.
   /// Only present for user-defined bindings for tuple-like types.
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 3ad71a223903c..da5a94bf5b59f 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -2840,7 +2840,8 @@ ExpectedDecl 
ASTNodeImporter::VisitBindingDecl(BindingDecl *D) {
   Error Err = Error::success();
   QualType ToType = importChecked(Err, D->getType());
   Expr *ToBinding = importChecked(Err, D->getBinding());
-  ValueDecl *ToDecomposedDecl = importChecked(Err, D->getDecomposedDecl());
+  DecompositionDecl *ToDecomposedDecl =
+      importChecked(Err, D->getDecomposedDecl());
   if (Err)
     return std::move(Err);
 
diff --git a/clang/lib/AST/ComputeDependence.cpp 
b/clang/lib/AST/ComputeDependence.cpp
index a819bb6dec599..0874dc6da381d 100644
--- a/clang/lib/AST/ComputeDependence.cpp
+++ b/clang/lib/AST/ComputeDependence.cpp
@@ -616,6 +616,31 @@ ExprDependence clang::computeDependence(DeclRefExpr *E, 
const ASTContext &Ctx) {
       Deps |= ExprDependence::ValueInstantiation;
   }
 
+  // The standard doesn't explicitly specify rules for when individial bindings
+  // a structured binding declaration are value-dependent. Handle them using a
+  // similar rule to the rule for variables:
+  //
+  // - An id-expression referring to a tuple binding is value-dependent if
+  //   an id-expression referring to the synthetic variable used to store the
+  //   result of get() would be value-dependent.
+  // - An id-expression referring to a non-tuple binding is value-dependent if
+  //   an id-expression referring to the synthetic variable used to store the
+  //   initializer would be value-dependent.
+  //
+  // Internally, this is equivalent to just checking whether the expression
+  // representing the binding is value-dependent.
+  if (const auto *BD = dyn_cast<BindingDecl>(Decl)) {
+    if (const Expr *Init = BD->getBinding()) {
+      if (Init->containsErrors())
+        Deps |= ExprDependence::Error;
+
+      if (Init->isValueDependent())
+        Deps |= ExprDependence::ValueInstantiation;
+    }
+
+    return Deps;
+  }
+
   return Deps;
 }
 
diff --git a/clang/test/SemaCXX/binding-value-dependence.cpp 
b/clang/test/SemaCXX/binding-value-dependence.cpp
new file mode 100644
index 0000000000000..124e7eb3782a5
--- /dev/null
+++ b/clang/test/SemaCXX/binding-value-dependence.cpp
@@ -0,0 +1,36 @@
+// RUN: %clang_cc1 -fsyntax-only -std=c++26 -verify %s
+
+// expected-no-diagnostics
+
+template<int (*a)[3]> void pr211930() {
+  auto&& [x, y, z] = *a;
+  // This shouldn't crash; x is value-dependent.
+  constexpr int q = x;
+}
+
+struct S {int a = 4;};
+namespace std {
+    template <typename T> struct tuple_size;
+    template <> struct tuple_size<S> { static const int value = 3; };
+    template <> struct tuple_size<const S> { static const int value = 3; };
+    template <int I, typename T> struct tuple_element;
+    template <int I> struct tuple_element<I, S> {
+        using type = const int;
+    };
+    template <int I> struct tuple_element<I, const S> {
+        using type = const int;
+    };
+}
+static const int Z = 4;
+template<int x> constexpr const int &get(S&&s) { return s.a; }
+template<int x> constexpr const int &get(const S&s) { return s.a; }
+template<S *s> void value_dependent_get() {
+  auto &[a,b,c] = *s;
+  // This shouldn't warn: a is value-dependent.
+  int rr[-11/(a)];
+}
+template<const S *s> void constexpr_value_dependent_get() {
+  static constexpr auto [a,b,c] = *s;
+  // This shouldn't warn: a is value-dependent.
+  int rr[-11/(a)];
+}

>From d03c9a43faa391f927221d5c87c999fec9c701f1 Mon Sep 17 00:00:00 2001
From: Eli Friedman <[email protected]>
Date: Tue, 28 Jul 2026 11:22:53 -0700
Subject: [PATCH 2/4] Adjust tests.

---
 .../test/SemaCXX/binding-value-dependence.cpp | 33 +++++++++++++++++--
 1 file changed, 31 insertions(+), 2 deletions(-)

diff --git a/clang/test/SemaCXX/binding-value-dependence.cpp 
b/clang/test/SemaCXX/binding-value-dependence.cpp
index 124e7eb3782a5..5d561c519cf6e 100644
--- a/clang/test/SemaCXX/binding-value-dependence.cpp
+++ b/clang/test/SemaCXX/binding-value-dependence.cpp
@@ -1,18 +1,33 @@
 // RUN: %clang_cc1 -fsyntax-only -std=c++26 -verify %s
 
-// expected-no-diagnostics
-
 template<int (*a)[3]> void pr211930() {
   auto&& [x, y, z] = *a;
   // This shouldn't crash; x is value-dependent.
   constexpr int q = x;
+
+  // Variations using different forms of initialization.
+  auto&& [x2, y2, z2]{*a};
+  constexpr int q2 = x2;
+  auto&& [x3, y3, z3](*a);
+  constexpr int q3 = x3;
+}
+
+template<int (*a)[3]> void not_value_dependent() {
+  auto [x, y, z] = *a;
+  constexpr int c = &x == &x+1;
+  switch (1) {
+    case c:;  // expected-note {{previous case defined here}}
+    case 0:;  // expected-error {{duplicate case value: 'c' and '0' both equal 
'0'}}
+  }
 }
 
 struct S {int a = 4;};
+struct S2 { };
 namespace std {
     template <typename T> struct tuple_size;
     template <> struct tuple_size<S> { static const int value = 3; };
     template <> struct tuple_size<const S> { static const int value = 3; };
+    template <> struct tuple_size<S2> { static const int value = 3; };
     template <int I, typename T> struct tuple_element;
     template <int I> struct tuple_element<I, S> {
         using type = const int;
@@ -20,10 +35,14 @@ namespace std {
     template <int I> struct tuple_element<I, const S> {
         using type = const int;
     };
+    template <int I> struct tuple_element<I, S2> {
+        using type = const int;
+    };
 }
 static const int Z = 4;
 template<int x> constexpr const int &get(S&&s) { return s.a; }
 template<int x> constexpr const int &get(const S&s) { return s.a; }
+template<int x> constexpr const int get(const S2&s) { return 4; }
 template<S *s> void value_dependent_get() {
   auto &[a,b,c] = *s;
   // This shouldn't warn: a is value-dependent.
@@ -34,3 +53,13 @@ template<const S *s> void constexpr_value_dependent_get() {
   // This shouldn't warn: a is value-dependent.
   int rr[-11/(a)];
 }
+template<const S2 *s> void constexpr_non_value_dependent_get() {
+  static auto [a,b,c] = *s;
+  // The variable holding the initializer is not potentially-constant, so
+  // the variable holding the return value of get() is a non-value-dependent
+  // constant.
+  switch (1) {
+    case a: ; // expected-note {{previous case defined here}}
+    case b: ; // expected-error {{duplicate case value: 'a' and 'b' both equal 
'4'}}
+  }
+}

>From e4b3bc3deeee3c23a70dc1ff8520cd8cfcebd02f Mon Sep 17 00:00:00 2001
From: Eli Friedman <[email protected]>
Date: Wed, 5 Aug 2026 14:39:32 -0700
Subject: [PATCH 3/4] Review comment

---
 clang/lib/AST/ComputeDependence.cpp | 2 --
 1 file changed, 2 deletions(-)

diff --git a/clang/lib/AST/ComputeDependence.cpp 
b/clang/lib/AST/ComputeDependence.cpp
index 0874dc6da381d..7e6bd69711c5b 100644
--- a/clang/lib/AST/ComputeDependence.cpp
+++ b/clang/lib/AST/ComputeDependence.cpp
@@ -637,8 +637,6 @@ ExprDependence clang::computeDependence(DeclRefExpr *E, 
const ASTContext &Ctx) {
       if (Init->isValueDependent())
         Deps |= ExprDependence::ValueInstantiation;
     }
-
-    return Deps;
   }
 
   return Deps;

>From 98db6f9a5ff171afd5160b120bda6f496ee0197b Mon Sep 17 00:00:00 2001
From: Eli Friedman <[email protected]>
Date: Fri, 7 Aug 2026 11:50:02 -0700
Subject: [PATCH 4/4] Release note

---
 clang/docs/ReleaseNotes.md | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 804f93cc37e68..c3507306fc321 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -416,6 +416,10 @@ features cannot lower the translation-unit ABI level;
   libstdc++15 has been extended to support preprocessed input. Previously, 
splitting the preprocessing and
   compilation step would result in the fix not being applied. (#GH160314)
 
+- Compute value dependence correctly for structured bindings. This mostly
+  affect C++26 constexpr structured bindings and expansion statements, but
+  also affects some uses of plain structured bindings. (#GH211930)
+
 #### Bug Fixes to AST Handling
 
 - Fixed a non-deterministic ordering of unused local typedefs that made

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to