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
