[clang] [Clang][Sema] Fix an ICE where structured binding packs within a lambda should be diagnosed immediately (PR #214716)
via cfe-commits
cfe-commits at lists.llvm.org
Sat Aug 8 07:58:22 PDT 2026
https://github.com/babadany2999 updated https://github.com/llvm/llvm-project/pull/214716
>From af68f517aefadb3c4f49aa1cff879328a8de64fa Mon Sep 17 00:00:00 2001
From: Baba Dan Constantin <babadany2999 at gmail.com>
Date: Sat, 8 Aug 2026 16:23:15 +0300
Subject: [PATCH] [Clang][Sema] Fix an ICE where structured binding packs
within a lambda should be diagnosed immediately. (#214160)
Signed-off-by: Baba Dan Constantin <babadany2999 at gmail.com>
---
clang/docs/ReleaseNotes.md | 3 +
clang/lib/Sema/SemaDeclCXX.cpp | 6 +
clang/test/SemaCXX/GH214160.cpp | 291 ++++++++++++++++++++++++++++++++
3 files changed, 300 insertions(+)
create mode 100644 clang/test/SemaCXX/GH214160.cpp
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index a00b725143d49..42de9d57d5e80 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -366,6 +366,9 @@ features cannot lower the translation-unit ABI level;
- Fixed USR generation for declarations whose signature mentions a class-type
non-type template parameter. (#GH212351)
- Clang now defines the GCC-compatible predefined macro `__SIG_ATOMIC_TYPE__`. (#GH213895)
+- Fixed an ICE where structured binding packs within a lambda were
+ considered for delayed diagnostics, when they should be diagnosed
+ immediately. (#GH214160)
#### Bug Fixes to Compiler Builtins
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 47b01b913b428..66c77a63fced6 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -914,6 +914,12 @@ Sema::ActOnDecompositionDeclarator(Scope *S, Declarator &D,
auto *BD = BindingDecl::Create(Context, DC, B.NameLoc, B.Name, QT);
+ if (BD->isParameterPack()) {
+ if (auto *CSI = getEnclosingLambdaOrBlock()) {
+ CSI->LocalPacks.push_back(BD);
+ }
+ }
+
ProcessDeclAttributeList(S, BD, *B.Attrs);
// Find the shadowed declaration before filtering for scope.
diff --git a/clang/test/SemaCXX/GH214160.cpp b/clang/test/SemaCXX/GH214160.cpp
new file mode 100644
index 0000000000000..bab9d69de122c
--- /dev/null
+++ b/clang/test/SemaCXX/GH214160.cpp
@@ -0,0 +1,291 @@
+// RUN: %clang_cc1 -std=c++2c -fsyntax-only -verify %s
+
+namespace GH214160 {
+struct A {
+int x, y;
+};
+
+template <typename = void>
+void non_constexpr_right_unary() {
+ ([&]{ auto [...tmp] = A{}; tmp; }(), ...);
+ // expected-error at -1 {{expression contains unexpanded parameter pack 'tmp'}}
+ // expected-error at -2 {{pack expansion does not contain any unexpanded parameter packs}}
+}
+
+template void non_constexpr_right_unary<void>();
+
+template <typename = void>
+constexpr void constexpr_right_unary() {
+ ([&]{ auto [...tmp] = A{}; tmp; }(), ...);
+ // expected-error at -1 {{expression contains unexpanded parameter pack 'tmp'}}
+ // expected-error at -2 {{pack expansion does not contain any unexpanded parameter packs}}
+}
+
+template void constexpr_right_unary<void>();
+
+template <typename = void>
+void non_constexpr_left_unary() {
+ (..., [&]{ auto [...tmp] = A{}; tmp; }());
+ // expected-error at -1 {{expression contains unexpanded parameter pack 'tmp'}}
+ // expected-error at -2 {{pack expansion does not contain any unexpanded parameter packs}}
+}
+
+template void non_constexpr_left_unary<void>();
+
+template <typename = void>
+constexpr void constexpr_left_unary() {
+ (..., [&]{ auto [...tmp] = A{}; tmp; }());
+ // expected-error at -1 {{expression contains unexpanded parameter pack 'tmp'}}
+ // expected-error at -2 {{pack expansion does not contain any unexpanded parameter packs}}
+}
+
+template void constexpr_left_unary<void>();
+
+// When pack is already expanded ([...tmp])
+template <typename = void>
+void non_constexpr_right_unary_expanded() {
+ ([&]{ auto [...tmp] = A{}; ((void)tmp, ...); }(), ...);
+ // expected-error at -1 {{pack expansion does not contain any unexpanded parameter packs}}
+}
+
+template void non_constexpr_right_unary_expanded<void>();
+
+template <typename = void>
+constexpr void constexpr_right_unary_expanded() {
+ ([&]{ auto [...tmp] = A{}; ((void)tmp, ...); }(), ...);
+ // expected-error at -1 {{pack expansion does not contain any unexpanded parameter packs}}
+}
+
+template void constexpr_right_unary_expanded<void>();
+
+template <typename = void>
+void non_constexpr_left_unary_expanded() {
+ (..., [&]{ auto [...tmp] = A{}; ((void)tmp, ...); }());
+ // expected-error at -1 {{pack expansion does not contain any unexpanded parameter packs}}
+}
+
+template void non_constexpr_left_unary_expanded<void>();
+
+template <typename = void>
+constexpr void constexpr_left_unary_expanded() {
+ (..., [&]{ auto [...tmp] = A{}; ((void)tmp, ...); }());
+ // expected-error at -1 {{pack expansion does not contain any unexpanded parameter packs}}
+}
+
+template void constexpr_left_unary_expanded<void>();
+
+template <typename = void>
+void non_constexpr_right_binary() {
+ ([&]{ auto [...tmp] = A{}; tmp; }() + ... + 0);
+ // expected-error at -1 {{expression contains unexpanded parameter pack 'tmp'}}
+ // expected-error at -2 {{pack expansion does not contain any unexpanded parameter packs}}
+}
+
+template void non_constexpr_right_binary<void>();
+
+template <typename = void>
+constexpr void constexpr_right_binary() {
+ ([&]{ auto [...tmp] = A{}; tmp; }() + ... + 0);
+ // expected-error at -1 {{expression contains unexpanded parameter pack 'tmp'}}
+ // expected-error at -2 {{pack expansion does not contain any unexpanded parameter packs}}
+}
+
+template void constexpr_right_binary<void>();
+
+template <typename = void>
+void non_constexpr_left_binary() {
+ (0 + ... + [&]{ auto [...tmp] = A{}; tmp; }());
+ // expected-error at -1 {{expression contains unexpanded parameter pack 'tmp'}}
+ // expected-error at -2 {{pack expansion does not contain any unexpanded parameter packs}}
+}
+
+template void non_constexpr_left_binary<void>();
+
+template <typename = void>
+constexpr void constexpr_left_binary() {
+ (0 + ... + [&]{ auto [...tmp] = A{}; tmp; }());
+ // expected-error at -1 {{expression contains unexpanded parameter pack 'tmp'}}
+ // expected-error at -2 {{pack expansion does not contain any unexpanded parameter packs}}
+}
+
+template void constexpr_left_binary<void>();
+
+// When pack is already expanded ([...tmp])
+template <typename = void>
+void non_constexpr_right_binary_expanded() {
+ ([&]{ auto [...tmp] = A{}; ((void)tmp, ...); }() + ... + 0);
+ // expected-error at -1 {{pack expansion does not contain any unexpanded parameter packs}}
+}
+
+template void non_constexpr_right_binary_expanded<void>();
+
+template <typename = void>
+constexpr void constexpr_right_binary_expanded() {
+ ([&]{ auto [...tmp] = A{}; ((void)tmp, ...); }() + ... + 0);
+ // expected-error at -1 {{pack expansion does not contain any unexpanded parameter packs}}
+}
+
+template void constexpr_right_binary_expanded<void>();
+
+template <typename = void>
+void non_constexpr_left_binary_expanded() {
+ (0 + ... + [&]{ auto [...tmp] = A{}; ((void)tmp, ...); }());
+ // expected-error at -1 {{pack expansion does not contain any unexpanded parameter packs}}
+}
+
+template void non_constexpr_left_binary_expanded<void>();
+
+template <typename = void>
+constexpr void constexpr_left_binary_expanded() {
+ (0 + ... + [&]{ auto [...tmp] = A{}; ((void)tmp, ...); }());
+ // expected-error at -1 {{pack expansion does not contain any unexpanded parameter packs}}
+}
+
+template void constexpr_left_binary_expanded<void>();
+
+// The following should produce no unexpanded pack errors as they are valid expressions
+
+template <typename = void>
+void non_constexpr_right_unary_same_scope() {
+ auto [...tmp] = A{};
+ ([&]{ (void)tmp; }(), ...);
+}
+
+template void non_constexpr_right_unary_same_scope<void>();
+
+template <typename = void>
+constexpr void constexpr_right_unary_same_scope() {
+ auto [...tmp] = A{};
+ ([&]{ (void)tmp; }(), ...);
+}
+
+template void constexpr_right_unary_same_scope<void>();
+
+template <typename = void>
+void non_constexpr_left_unary_same_scope() {
+ auto [...tmp] = A{};
+ (..., [&]{ (void)tmp; }());
+}
+
+template void non_constexpr_left_unary_same_scope<void>();
+
+template <typename = void>
+constexpr void constexpr_left_unary_same_scope() {
+ auto [...tmp] = A{};
+ (..., [&]{ (void)tmp; }());
+}
+
+template void constexpr_left_unary_same_scope<void>();
+
+template <typename = void>
+void non_constexpr_right_binary_same_scope() {
+ auto [...tmp] = A{};
+ (void)([&]{ (void)tmp; return 0; }() + ... + 0);
+}
+
+template void non_constexpr_right_binary_same_scope<void>();
+
+template <typename = void>
+constexpr void constexpr_right_binary_same_scope() {
+ auto [...tmp] = A{};
+ (void)([&]{ (void)tmp; return 0; }() + ... + 0);
+}
+
+template void constexpr_right_binary_same_scope<void>();
+
+template <typename = void>
+void non_constexpr_left_binary_same_scope() {
+ auto [...tmp] = A{};
+ (void)(0 + ... + [&]{ (void)tmp; return 0; }());
+}
+
+template void non_constexpr_left_binary_same_scope<void>();
+
+template <typename = void>
+constexpr void constexpr_left_binary_same_scope() {
+ auto [...tmp] = A{};
+ (void)(0 + ... + [&]{ (void)tmp; return 0; }());
+}
+
+template void constexpr_left_binary_same_scope<void>();
+
+template <typename = void>
+void non_constexpr_right_unary_same_scope_enclosed() {
+ [] {
+ auto [...tmp] = A{};
+ ([&]{ (void)tmp; }(), ...);
+ }();
+}
+
+template void non_constexpr_right_unary_same_scope_enclosed<void>();
+
+template <typename = void>
+constexpr void constexpr_right_unary_same_scope_enclosed() {
+ [] {
+ auto [...tmp] = A{};
+ ([&]{ (void)tmp; }(), ...);
+ }();
+}
+
+template void constexpr_right_unary_same_scope_enclosed<void>();
+
+template <typename = void>
+void non_constexpr_left_unary_same_scope_enclosed() {
+ [] {
+ auto [...tmp] = A{};
+ (..., [&]{ (void)tmp; }());
+ }();
+}
+
+template void non_constexpr_left_unary_same_scope_enclosed<void>();
+
+template <typename = void>
+constexpr void constexpr_left_unary_same_scope_enclosed() {
+ [] {
+ auto [...tmp] = A{};
+ (..., [&]{ (void)tmp; }());
+ }();
+}
+
+template void constexpr_left_unary_same_scope_enclosed<void>();
+
+template <typename = void>
+void non_constexpr_right_binary_same_scope_enclosed() {
+ [] {
+ auto [...tmp] = A{};
+ (void)([&]{ (void)tmp; return 0; }() + ... + 0);
+ }();
+}
+
+template void non_constexpr_right_binary_same_scope_enclosed<void>();
+
+template <typename = void>
+constexpr void constexpr_right_binary_same_scope_enclosed() {
+ [] {
+ auto [...tmp] = A{};
+ (void)([&]{ (void)tmp; return 0; }() + ... + 0);
+ }();
+}
+
+template void constexpr_right_binary_same_scope_enclosed<void>();
+
+template <typename = void>
+void non_constexpr_left_binary_same_scope_enclosed() {
+ [] {
+ auto [...tmp] = A{};
+ (void)(0 + ... + [&]{ (void)tmp; return 0; }());
+ }();
+}
+
+template void non_constexpr_left_binary_same_scope_enclosed<void>();
+
+template <typename = void>
+constexpr void constexpr_left_binary_same_scope_enclosed() {
+ [] {
+ auto [...tmp] = A{};
+ (void)(0 + ... + [&]{ (void)tmp; return 0; }());
+ }();
+}
+
+template void constexpr_left_binary_same_scope_enclosed<void>();
+}
More information about the cfe-commits
mailing list