Author: Matthias Wippich
Date: 2026-05-26T17:51:19+02:00
New Revision: 26f19eed998d513c1200f46733db14d73e2b3a71

URL: 
https://github.com/llvm/llvm-project/commit/26f19eed998d513c1200f46733db14d73e2b3a71
DIFF: 
https://github.com/llvm/llvm-project/commit/26f19eed998d513c1200f46733db14d73e2b3a71.diff

LOG: [clang] propagate constexpr/constinit to binding VarDecl (#195860)

This patch implements one of the missing parts of P2686. This is
required to make the test case from
[cwg3135](https://cplusplus.github.io/CWG/issues/3135.html) (and
likewise the examples of [P1789](https://wg21.link/p1789) work).

Added: 
    

Modified: 
    clang/docs/ReleaseNotes.rst
    clang/lib/Sema/SemaDeclCXX.cpp
    clang/test/SemaCXX/cxx2c-decomposition.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index fb70b83d6a7c4..6d917755bab17 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -189,6 +189,8 @@ C++ Language Changes
 C++2c Feature Support
 ^^^^^^^^^^^^^^^^^^^^^
 
+- Clang now propagates ``constinit`` and ``constexpr`` in structured bindings 
with tuple-like initializers.
+
 C++23 Feature Support
 ^^^^^^^^^^^^^^^^^^^^^
 

diff  --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index de837ff0608d0..daebee54feea7 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -1405,6 +1405,9 @@ static bool checkTupleLikeDecomposition(Sema &S,
                         /*TInfo=*/nullptr, Src->getStorageClass());
     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 df2e3fa90263a..2ab26b1313518 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 }; };
@@ -40,7 +44,7 @@ static_assert(t3 == 2);
 
 constexpr auto [t4] = 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}}
+//   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}}
 
 constexpr auto [t5] = Z();
 static_assert(t5 == 43);
@@ -62,15 +66,92 @@ void test() {
     test_tpl(0);
 }
 
-// FIXME : support tuple
-constexpr auto [a, b] = B{};
+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}} \
-// 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}}
+constinit auto [init2] = X {};  
+// expected-error@-1 {{variable does not have a constant initializer}} \
+//   expected-note@-1 {{required by 'constinit' specifier here}} \
+//   expected-note@-1 {{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}}
+
+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-note@-1 {{in instantiation of function template specialization 
'get<0>' requested here}} \
+//   expected-note@-1 {{in implicit initialization of binding declaration 
'e1'}} \
+// expected-error@-1 {{constexpr variable 'e1' must be initialized by a 
constant expression}} \
+//   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