[clang] [clang][constexpr] Fix assertion failure in C++26 constexpr structured binding pack evaluation (#170991) (PR #213534)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Aug 5 11:49:06 PDT 2026
https://github.com/babadany2999 updated https://github.com/llvm/llvm-project/pull/213534
>From 24c61e8709fcc5d656c1c8379d61846320389896 Mon Sep 17 00:00:00 2001
From: Baba Dan Constantin <babadany2999 at gmail.com>
Date: Sun, 2 Aug 2026 14:35:25 +0300
Subject: [PATCH] [clang][constexpr] Fix assertion failure in constexpr
structured binding pack evaluation (#GH170991)
---
clang/docs/ReleaseNotes.md | 3 +
clang/lib/Sema/SemaDeclCXX.cpp | 3 +
.../cxx2c-binding-pack-constexpr-crash.cpp | 111 ++++++++++++++++++
3 files changed, 117 insertions(+)
create mode 100644 clang/test/SemaCXX/cxx2c-binding-pack-constexpr-crash.cpp
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 7108392abbaa1..3d73e420fe5b4 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -424,6 +424,9 @@ features cannot lower the translation-unit ABI level;
copy so the union's object representation is copied, matching the defaulted
union copy constructor.
+- Fixed an assertion failure when evaluating C++26 `constexpr` structured binding packs during template
+ instantiation. (#GH170991)
+
#### Bug Fixes to AST Handling
- Fixed a non-deterministic ordering of unused local typedefs that made
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 47b01b913b428..645994603dabd 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -1655,6 +1655,7 @@ void Sema::CheckCompleteDecompositionDeclaration(DecompositionDecl *DD) {
if (auto *CAT = Context.getAsConstantArrayType(DecompType)) {
if (checkArrayDecomposition(*this, Bindings, DD, DecompType, CAT))
DD->setInvalidDecl();
+ CleanupVarDeclMarking();
return;
}
if (auto *VT = DecompType->getAs<VectorType>()) {
@@ -1680,6 +1681,7 @@ void Sema::CheckCompleteDecompositionDeclaration(DecompositionDecl *DD) {
case IsTupleLike::TupleLike:
if (checkTupleLikeDecomposition(*this, Bindings, DD, DecompType, TupleSize))
DD->setInvalidDecl();
+ CleanupVarDeclMarking();
return;
case IsTupleLike::NotTupleLike:
@@ -1701,6 +1703,7 @@ void Sema::CheckCompleteDecompositionDeclaration(DecompositionDecl *DD) {
// E or of the same unambiguous public base class of E, ...
if (checkMemberDecomposition(*this, Bindings, DD, DecompType, RD))
DD->setInvalidDecl();
+ CleanupVarDeclMarking();
}
UnsignedOrNone Sema::GetDecompositionElementCount(QualType T,
diff --git a/clang/test/SemaCXX/cxx2c-binding-pack-constexpr-crash.cpp b/clang/test/SemaCXX/cxx2c-binding-pack-constexpr-crash.cpp
new file mode 100644
index 0000000000000..6c41e4f332749
--- /dev/null
+++ b/clang/test/SemaCXX/cxx2c-binding-pack-constexpr-crash.cpp
@@ -0,0 +1,111 @@
+// RUN: %clang -fsyntax-only -std=c++26 %s
+
+namespace GH170991 {
+// Test case: struct
+struct S { int x{}; };
+
+template <typename = void>
+void f() {
+ constexpr S s;
+ constexpr auto [x] = s;
+ constexpr auto [...xs] = s;
+}
+
+template void f<void>();
+
+// Test case: array
+template <typename = void>
+void g() {
+ constexpr int a[2]{};
+ constexpr auto [x, y] = a;
+ constexpr auto [...xs] = a;
+}
+
+template void g<void>();
+} // namespace GH170991
+
+// Test case: tuple-like
+namespace std {
+template <typename T>
+struct tuple_size;
+template <unsigned I, typename T>
+struct tuple_element;
+} // namespace std
+
+namespace GH170991 {
+struct TupleLikeFnTemplate {
+ int x = 100;
+ char y = 'D';
+
+ // If a search for the name get in the scope of E [...] the initializer is e.get<i>()
+ template <unsigned I>
+ constexpr decltype(auto) get() const& {
+ if constexpr (I == 0) return 500;
+ else return y;
+ }
+};
+
+struct TupleLikeADL {
+ int x = 100;
+ char y = 'D';
+};
+
+// Otherwise, the initializer is get<i>(e)[...]
+template <unsigned I>
+constexpr decltype(auto) get(const TupleLikeADL& t) {
+ if constexpr (I == 0) return 500;
+ else return t.y;
+}
+} // namespace GH170991
+
+namespace std {
+template <>
+struct tuple_size<const GH170991::TupleLikeFnTemplate> {
+ static constexpr unsigned value = 2;
+};
+template <>
+struct tuple_size<const GH170991::TupleLikeADL> {
+ static constexpr unsigned value = 2;
+};
+
+template <>
+struct tuple_element<0, const GH170991::TupleLikeFnTemplate> {
+ using type = int;
+};
+template <>
+struct tuple_element<0, const GH170991::TupleLikeADL> {
+ using type = int;
+};
+
+template <>
+struct tuple_element<1, const GH170991::TupleLikeFnTemplate> {
+ using type = char;
+};
+template <>
+struct tuple_element<1, const GH170991::TupleLikeADL> {
+ using type = char;
+};
+} // namespace std
+
+namespace GH170991 {
+template <typename = void>
+void h() {
+ constexpr TupleLikeFnTemplate fn_template{};
+ constexpr auto [x, y] = fn_template;
+ static_assert(x == 500); // Proves tuple_like.get<0>(tuple-like decomposition) was called
+ static_assert(y == 'D');
+ constexpr auto [...xs] = fn_template;
+ static_assert(xs...[0] == 500);
+ static_assert(xs...[1] == 'D');
+
+ constexpr TupleLikeADL adl{};
+ constexpr auto [x2, y2] = adl;
+ static_assert(x2 == 500); // Proves get<0>(tuple-like decomposition) was called
+ static_assert(y2 == 'D');
+ constexpr auto [...xs2] = adl;
+ static_assert(xs2...[0] == 500);
+ static_assert(xs2...[1] == 'D');
+}
+
+template void h<void>();
+} // namespace GH170991
More information about the cfe-commits
mailing list