[clang] [clang] Compute value dependence for references to structured bindings (PR #212368)
Eli Friedman via cfe-commits
cfe-commits at lists.llvm.org
Mon Jul 27 15:50:09 PDT 2026
https://github.com/efriedma-quic created https://github.com/llvm/llvm-project/pull/212368
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
>From 3e1d70868fd66953d97fd9f1e8b0a9010f1f4a39 Mon Sep 17 00:00:00 2001
From: Eli Friedman <efriedma at qti.qualcomm.com>
Date: Mon, 27 Jul 2026 15:40:05 -0700
Subject: [PATCH] [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 db7d223d56af3..21dd219af6ca9 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -2835,7 +2835,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)];
+}
More information about the cfe-commits
mailing list