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
