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/4] [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/4] 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/4] 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}} >From ccc087702a425ec28a02e6833d1fc0eea551e1bd Mon Sep 17 00:00:00 2001 From: Matthias Wippich <[email protected]> Date: Tue, 26 May 2026 01:57:19 +0200 Subject: [PATCH 4/4] fix indentation --- clang/test/SemaCXX/cxx2c-decomposition.cpp | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/clang/test/SemaCXX/cxx2c-decomposition.cpp b/clang/test/SemaCXX/cxx2c-decomposition.cpp index d3db62387eced..2ab26b1313518 100644 --- a/clang/test/SemaCXX/cxx2c-decomposition.cpp +++ b/clang/test/SemaCXX/cxx2c-decomposition.cpp @@ -44,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); @@ -70,10 +70,11 @@ constexpr auto [a, b] = B {}; static_assert(a.n == 0); 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'}} @@ -148,9 +149,9 @@ constexpr auto const& get(E const& obj) { 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 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}} +// 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
