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 <[email protected]> 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)]; +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
