[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
Sun Aug 2 05:00:11 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: babadany2999

<details>
<summary>Changes</summary>

### Summary
Fixed an assertion failure (`Assertion 'MaybeODRUseExprs.empty() && "Leftover expressions for odr-use checking"' failed`) when evaluating C++26 `constexpr` structured binding packs during template instantiation.

Fixes #<!-- -->170991.

### Root Cause
When instantiating a C++26 `constexpr` structured binding pack (e.g. `constexpr auto [...xs] = s;`), building the member binding expressions in `checkMemberDecomposition` creates `DeclRefExpr` nodes referencing the `DecompositionDecl`.

Because the declaration is `constexpr`, `BuildDeclRefExpr` defers ODR-use tracking and registers these `DeclRefExpr` nodes in `Sema::MaybeODRUseExprs`.

However, `CheckCompleteDecompositionDeclaration` is executed after `ActOnFinishFullExpr` has already run for the variable initializer. As a result, `MaybeODRUseExprs` was left unflushed after decomposition bindings were created. When `ActOnFinishFunctionBody` completed the function body, it encountered leftover expressions in `MaybeODRUseExprs` and failed the assertion.

### Solution
Added a call to `CleanupVarDeclMarking()` at the end of `Sema::CheckCompleteDecompositionDeclaration` in `clang/lib/Sema/SemaDeclCXX.cpp`. This flushes `MaybeODRUseExprs` and completes ODR-use tracking for the decomposition declaration bindings before returning.

### Test Plan
- Added regression test case to `clang/test/SemaCXX/cxx2c-binding-pack-constexpr-crash.cpp`.
- Verified the test fails with an assertion failure on unpatched Clang and passes cleanly with the patch.
- Ran full `clang/test/SemaCXX` and `clang/test/CXX` test suites.
- Added release note to `clang/docs/ReleaseNotes.md` under *Bug Fixes to C++ Support*.

---
Full diff: https://github.com/llvm/llvm-project/pull/213534.diff


3 Files Affected:

- (modified) clang/docs/ReleaseNotes.md (+3) 
- (modified) clang/lib/Sema/SemaDeclCXX.cpp (+2) 
- (added) clang/test/SemaCXX/cxx2c-binding-pack-constexpr-crash.cpp (+13) 


``````````diff
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index a38b99ff8e075..a169d065605e4 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..ebc1d8ba0008f 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -1701,6 +1701,8 @@ 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..7ce4c5216ac94
--- /dev/null
+++ b/clang/test/SemaCXX/cxx2c-binding-pack-constexpr-crash.cpp
@@ -0,0 +1,13 @@
+// RUN: %clang_cc1 -fsyntax-only -std=c++26 %s
+
+namespace GH170991 {
+struct S { int x{}; };
+
+template <typename = void>
+void f() {
+  constexpr S s;
+  constexpr auto [...xs] = s;
+}
+
+template void f<void>();
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/213534


More information about the cfe-commits mailing list