https://github.com/Tsche updated 
https://github.com/llvm/llvm-project/pull/195860

>From 6d097f80f50457032e2fc2f80bfa6f1f7337716c Mon Sep 17 00:00:00 2001
From: Matthias Wippich <[email protected]>
Date: Mon, 4 May 2026 05:30:53 +0200
Subject: [PATCH 1/3] [clang] propagate constexpr to binding VarDecl

---
 clang/lib/Sema/SemaDeclCXX.cpp             | 1 +
 clang/test/SemaCXX/cxx2c-decomposition.cpp | 4 ----
 2 files changed, 1 insertion(+), 4 deletions(-)

diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index de837ff0608d0..39c3aa2cdc2c0 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -1405,6 +1405,7 @@ static bool checkTupleLikeDecomposition(Sema &S,
                         /*TInfo=*/nullptr, Src->getStorageClass());
     BindingVD->setLexicalDeclContext(Src->getLexicalDeclContext());
     BindingVD->setTSCSpec(Src->getTSCSpec());
+    BindingVD->setConstexpr(Src->isConstexpr());
     BindingVD->setImplicit();
     if (Src->isInlineSpecified())
       BindingVD->setInlineSpecified();
diff --git a/clang/test/SemaCXX/cxx2c-decomposition.cpp 
b/clang/test/SemaCXX/cxx2c-decomposition.cpp
index df2e3fa90263a..3e7fdfc904958 100644
--- a/clang/test/SemaCXX/cxx2c-decomposition.cpp
+++ b/clang/test/SemaCXX/cxx2c-decomposition.cpp
@@ -62,12 +62,8 @@ void test() {
     test_tpl(0);
 }
 
-// FIXME : support tuple
 constexpr auto [a, b] = B{};
 static_assert(a.n == 0);
-// expected-error@-1 {{static assertion expression is not an integral constant 
expression}} \
-// expected-note@-1 {{read of non-constexpr variable 'a' is not allowed in a 
constant expression}} \
-// expected-note@-2 {{declared here}}
 
 constinit auto [init1] = Y {42};
 constinit auto [init2] = X {};  // expected-error {{variable does not have a 
constant initializer}} \

>From a97a0e60677ab3f548ce458617ae54ce5d531e59 Mon Sep 17 00:00:00 2001
From: Matthias Wippich <[email protected]>
Date: Thu, 21 May 2026 23:00:23 +0200
Subject: [PATCH 2/3] add tests and release note

---
 clang/docs/ReleaseNotes.rst                |  2 +
 clang/lib/Sema/SemaDeclCXX.cpp             |  2 +
 clang/test/SemaCXX/cxx2c-decomposition.cpp | 62 +++++++++++++++++++++-
 3 files changed, 65 insertions(+), 1 deletion(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index a9017f44058be..27845822c785b 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -186,6 +186,8 @@ C++ Language Changes
 C++2c Feature Support
 ^^^^^^^^^^^^^^^^^^^^^
 
+- Clang now propagates ``constinit`` and ``constexpr`` in structured bindings 
with tuple-like initializers. (#GH195860)
+
 C++23 Feature Support
 ^^^^^^^^^^^^^^^^^^^^^
 
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 39c3aa2cdc2c0..daebee54feea7 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -1406,6 +1406,8 @@ static bool checkTupleLikeDecomposition(Sema &S,
     BindingVD->setLexicalDeclContext(Src->getLexicalDeclContext());
     BindingVD->setTSCSpec(Src->getTSCSpec());
     BindingVD->setConstexpr(Src->isConstexpr());
+    if (const auto *CIAttr = Src->getAttr<ConstInitAttr>())
+      BindingVD->addAttr(CIAttr->clone(S.Context));
     BindingVD->setImplicit();
     if (Src->isInlineSpecified())
       BindingVD->setInlineSpecified();
diff --git a/clang/test/SemaCXX/cxx2c-decomposition.cpp 
b/clang/test/SemaCXX/cxx2c-decomposition.cpp
index 3e7fdfc904958..7ff931c2bbd72 100644
--- a/clang/test/SemaCXX/cxx2c-decomposition.cpp
+++ b/clang/test/SemaCXX/cxx2c-decomposition.cpp
@@ -62,7 +62,7 @@ void test() {
     test_tpl(0);
 }
 
-constexpr auto [a, b] = B{};
+constexpr auto [a, b] = B {};
 static_assert(a.n == 0);
 
 constinit auto [init1] = Y {42};
@@ -70,3 +70,63 @@ constinit auto [init2] = X {};  // expected-error {{variable 
does not have a con
 // expected-note {{required by 'constinit' specifier here}} \
 // expected-note {{non-constexpr constructor 'X' cannot be used in a constant 
expression}} \
 // expected-note@#X-decl {{declared here}}
+
+constexpr auto [init3] = X {}; 
+// expected-error@-1 {{constexpr variable cannot have non-literal type 'const 
X'}}
+//   expected-note@#X-decl {{'X' is not literal because it is not an aggregate 
and has no constexpr constructors other than copy or move constructors}}
+
+struct C {};
+template<> struct std::tuple_size<C> { constexpr static auto value = 1; };
+template<> struct std::tuple_size<const C> { constexpr static auto value = 1; 
};
+template<> struct std::tuple_element<0, const C> { using type = int; };
+template<> struct std::tuple_element<0, C> { using type = int; };
+
+template<int N>
+auto get(C) { // #non-constexpr-get
+  return 0;
+};
+
+auto [c1] = C();
+static_assert(c1 == 0);
+// expected-error@-1 {{static assertion expression is not an integral constant 
expression}} \
+//   expected-note@-1 {{read of non-const variable 'c1' is not allowed in a 
constant expression}}
+//   expected-note@-4 {{declared here}}
+
+constexpr auto [c2] = C();
+// expected-error@-1 {{constexpr variable 'c2' must be initialized by a 
constant expression}} \
+//   expected-note@-1 {{in implicit initialization of binding declaration 
'c2'}} \
+//   expected-note@-1 {{non-constexpr function 'get<0>' cannot be used in a 
constant expression}} \
+//   expected-note@#non-constexpr-get {{declared here}}
+
+constinit auto [c3] = C();
+// expected-error@-1 {{variable does not have a constant initializer}} \
+//   expected-note@-1 {{in implicit initialization of binding declaration 
'c3'}} \
+//   expected-note@-1 {{required by 'constinit' specifier here}} \
+//   expected-note@-1 {{non-constexpr function 'get<0>' cannot be used in a 
constant expression}}
+//   expected-note@#non-constexpr-get {{declared here}}
+
+struct D {};
+template<> struct std::tuple_size<D> { constexpr static auto value = 1; };
+template<> struct std::tuple_size<const D> { constexpr static auto value = 1; 
};
+template<> struct std::tuple_element<0, D> { using type = int; };
+template<> struct std::tuple_element<0, const D> { using type = int; };
+
+template<int N>
+constexpr auto get(D) {
+  return 0;
+};
+
+auto [d1] = D();
+static_assert(d1 == 0);
+// expected-error@-1 {{static assertion expression is not an integral constant 
expression}} \
+//   expected-note@-1 {{read of non-const variable 'd1' is not allowed in a 
constant expression}}
+//   expected-note@-4 {{declared here}}
+
+constexpr auto [d2] = D();
+static_assert(d2 == 0);
+
+constinit auto [d3] = D();
+static_assert(d3 == 0);
+// expected-error@-1 {{static assertion expression is not an integral constant 
expression}} \
+//   expected-note@-1 {{read of non-const variable 'd3' is not allowed in a 
constant expression}}
+//   expected-note@-4 {{declared here}}

>From 719d7de0bab88266766cbffa5cfca89f8e54442a Mon Sep 17 00:00:00 2001
From: Matthias Wippich <[email protected]>
Date: Mon, 25 May 2026 23:53:21 +0200
Subject: [PATCH 3/3] address review comments

---
 clang/docs/ReleaseNotes.rst                |  2 +-
 clang/test/SemaCXX/cxx2c-decomposition.cpp | 24 ++++++++++++++++++++++
 2 files changed, 25 insertions(+), 1 deletion(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 27845822c785b..b5e7ebdfbce3e 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -186,7 +186,7 @@ C++ Language Changes
 C++2c Feature Support
 ^^^^^^^^^^^^^^^^^^^^^
 
-- Clang now propagates ``constinit`` and ``constexpr`` in structured bindings 
with tuple-like initializers. (#GH195860)
+- Clang now propagates ``constinit`` and ``constexpr`` in structured bindings 
with tuple-like initializers.
 
 C++23 Feature Support
 ^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/test/SemaCXX/cxx2c-decomposition.cpp 
b/clang/test/SemaCXX/cxx2c-decomposition.cpp
index 7ff931c2bbd72..d3db62387eced 100644
--- a/clang/test/SemaCXX/cxx2c-decomposition.cpp
+++ b/clang/test/SemaCXX/cxx2c-decomposition.cpp
@@ -16,6 +16,10 @@ struct Bit { constexpr Bit(): i(1), j(1){}; int i: 2; int 
j:2;};
 
 struct A { int a : 13; bool b; };
 
+constexpr auto [a0, a1] = A(42, true);
+static_assert(a0 == 42);
+static_assert(a1 == true);
+
 struct B {};
 template<> struct std::tuple_size<B> { enum { value = 2 }; };
 template<> struct std::tuple_size<const B> { enum { value = 2 }; };
@@ -130,3 +134,23 @@ static_assert(d3 == 0);
 // expected-error@-1 {{static assertion expression is not an integral constant 
expression}} \
 //   expected-note@-1 {{read of non-const variable 'd3' is not allowed in a 
constant expression}}
 //   expected-note@-4 {{declared here}}
+
+struct E { bool a: 1; };
+template<> struct std::tuple_size<E> { constexpr static auto value = 1; };
+template<> struct std::tuple_size<const E> { constexpr static auto value = 1; 
};
+template<> struct std::tuple_element<0, E> { using type = bool; };
+template<> struct std::tuple_element<0, const E> { using type = bool const; };
+
+template<int N>
+constexpr auto const& get(E const& obj) {
+  return obj.a; // #E-get
+};
+
+constexpr auto [e1] = E(true);
+// expected-error@#E-get {{returning reference to local temporary object}} \
+// expected-error@-1 {{constexpr variable 'e1' must be initialized by a 
constant expression}} \
+// expected-note@-1 {{in instantiation of function template specialization 
'get<0>' requested here}} \
+// expected-note@-1 {{in implicit initialization of binding declaration 'e1'}} 
\
+// expected-note@-1 {{in implicit initialization of binding declaration 'e1'}} 
\
+// expected-note@-1 {{reference to temporary is not a constant expression}} \
+// expected-note@#E-get {{temporary created here}}

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

Reply via email to