[clang] [Clang] Avoid numbering lambdas with invalid types (PR #232606)

Kunal Dubey via cfe-commits cfe-commits at lists.llvm.org
Thu Oct 15 23:08:29 PDT 2026


https://github.com/xakep8 created https://github.com/llvm/llvm-project/pull/232606

This is stacked on #232357. The first commit belongs to that PR; the lambda error-recovery change is the second commit.

During error recovery, a lambda parameter can contain an invalid type. Clang still tries to assign the lambda a stable number, which requires mangling its parameter types and can reach an internal compiler error.

Skip lambda numbering when the lambda's call-operator type already contains errors. This lets Clang report the original source error instead of crashing.

Fixes #170185


>From c51890dfb1393b549b93e583e8d7bdf0649ff77c Mon Sep 17 00:00:00 2001
From: Kunal Dubey <xakep8 at protonmail.com>
Date: Thu, 15 Oct 2026 20:13:08 +0530
Subject: [PATCH 1/2] [Clang] Fix mangling of parenthesized aggregate
 initialization

Clang did not correctly mangle parenthesized aggregate initializer nodes. Functional initializers could omit their arguments and produce duplicate names, while new expressions and other cast forms could reach an unreachable.\n\nMangle the user-written initializer arguments using the Itanium ABI expression encodings.
---
 clang/docs/ReleaseNotes.md             |  5 +++
 clang/lib/AST/ItaniumMangle.cpp        | 23 +++++++++++--
 clang/test/CodeGenCXX/mangle-cxx2a.cpp | 46 ++++++++++++++++++++++++++
 3 files changed, 72 insertions(+), 2 deletions(-)

diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index aba1dba5854fea..0d5ef39c8d6bd7 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -64,6 +64,11 @@ in a future version of Clang.
 
 ### ABI Changes in This Version
 
+- Clang now correctly mangles C++20 parenthesized aggregate initialization
+  expressions. Previously, initializer arguments could be omitted from the
+  mangled name, causing distinct entities to receive the same name.
+  (#GH231953)
+
 - Except on PlayStation, Clang now derives the x86-64 System V AVX ABI level
 for 256- and 512-bit vector arguments and returns from effective per-function
 target features. Features and `arch=` CPUs that imply AVX or AVX512F are
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index 0e32839a77be50..ddde9d48606df3 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -4945,7 +4945,13 @@ void CXXNameMangler::mangleCastExpression(const Expr *E, StringRef CastEncoding)
   const ExplicitCastExpr *ECE = cast<ExplicitCastExpr>(E);
   Out << CastEncoding;
   mangleType(ECE->getType());
-  mangleExpression(ECE->getSubExpr());
+  if (const auto *PLIE = dyn_cast<CXXParenListInitExpr>(ECE->getSubExpr())) {
+    ArrayRef<Expr *> Inits = PLIE->getUserSpecifiedInitExprs();
+    assert(Inits.size() == 1 && "cast must have exactly one operand");
+    mangleExpression(Inits.front());
+  } else {
+    mangleExpression(ECE->getSubExpr());
+  }
 }
 
 void CXXNameMangler::mangleInitListElements(const InitListExpr *InitList) {
@@ -5344,6 +5350,9 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity,
                                                   E = CCE->arg_end();
              I != E; ++I)
           mangleExpression(*I);
+      } else if (const auto *PLE = dyn_cast<CXXParenListInitExpr>(Init)) {
+        for (const Expr *Arg : PLE->getUserSpecifiedInitExprs())
+          mangleExpression(Arg);
       } else if (const ParenListExpr *PLE = dyn_cast<ParenListExpr>(Init)) {
         for (unsigned i = 0, e = PLE->getNumExprs(); i != e; ++i)
           mangleExpression(PLE->getExpr(i));
@@ -5770,7 +5779,17 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity,
         Sub = CCE->getArg(0)->IgnoreImplicit();
     if (auto *StdInitList = dyn_cast<CXXStdInitializerListExpr>(Sub))
       Sub = StdInitList->getSubExpr()->IgnoreImplicit();
-    if (auto *IL = dyn_cast<InitListExpr>(Sub)) {
+    if (auto *PLIE = dyn_cast<CXXParenListInitExpr>(Sub)) {
+      ArrayRef<Expr *> Inits = PLIE->getUserSpecifiedInitExprs();
+      Out << "cv";
+      mangleType(E->getType());
+      if (Inits.size() != 1)
+        Out << '_';
+      for (const Expr *Init : Inits)
+        mangleExpression(Init);
+      if (Inits.size() != 1)
+        Out << 'E';
+    } else if (auto *IL = dyn_cast<InitListExpr>(Sub)) {
       Out << "tl";
       mangleType(E->getType());
       mangleInitListElements(IL);
diff --git a/clang/test/CodeGenCXX/mangle-cxx2a.cpp b/clang/test/CodeGenCXX/mangle-cxx2a.cpp
index 3e4689948a7a7e..82dad5cdeb83b2 100644
--- a/clang/test/CodeGenCXX/mangle-cxx2a.cpp
+++ b/clang/test/CodeGenCXX/mangle-cxx2a.cpp
@@ -9,3 +9,49 @@ namespace spaceship {
   template<typename T> void f(decltype(Y() < X(), T()) x) {}
   template void f<int>(int);
 }
+
+namespace paren_init {
+  struct A { int a; };
+  struct B { int a, b; };
+  struct D { int a, b = 7; };
+  template <int> struct X {};
+
+  template <int N> void f(X<N + A(1).a>) {}
+  template <int N> void f(X<N + A(2).a>) {}
+
+  // CHECK-LABEL: define {{.*}} @_ZN10paren_init1fILi0EEEvNS_1XIXplT_dtcvNS_1AELi1E1aEEE
+  template void f<0>(X<1>);
+  // CHECK-LABEL: define {{.*}} @_ZN10paren_init1fILi0EEEvNS_1XIXplT_dtcvNS_1AELi2E1aEEE
+  template void f<0>(X<2>);
+
+  template <int N> void c_style(X<N + ((A)1).a>) {}
+  template <int N> void static_style(X<N + static_cast<A>(1).a>) {}
+
+  // CHECK-LABEL: define {{.*}} @_ZN10paren_init7c_styleILi0EEEvNS_1XIXplT_dtcvNS_1AELi1E1aEEE
+  template void c_style<0>(X<1>);
+  // CHECK-LABEL: define {{.*}} @_ZN10paren_init12static_styleILi0EEEvNS_1XIXplT_dtscNS_1AELi1E1aEEE
+  template void static_style<0>(X<1>);
+
+  // Check zero and multiple arguments, and a default member initializer.
+  template <int N> void zero(X<N + A().a>) {}
+  template <int N> void two(X<N + B(1, 2).b>) {}
+  template <int N> void defaulted(X<N + D(1).b>) {}
+
+  // CHECK-LABEL: define {{.*}} @_ZN10paren_init4zeroILi0EEEvNS_1XIXplT_dtcvNS_1AE_E1aEEE
+  template void zero<0>(X<0>);
+  // CHECK-LABEL: define {{.*}} @_ZN10paren_init3twoILi0EEEvNS_1XIXplT_dtcvNS_1BE_Li1ELi2EE1bEEE
+  template void two<0>(X<2>);
+  // CHECK-LABEL: define {{.*}} @_ZN10paren_init9defaultedILi0EEEvNS_1XIXplT_dtcvNS_1DELi1E1bEEE
+  template void defaulted<0>(X<7>);
+
+  template <int N> void new_zero(decltype((N, new A()))) {}
+  template <int N> void new_one(decltype((N, new A(1)))) {}
+  template <int N> void new_two(decltype((N, new B(1, 2)))) {}
+
+  // CHECK-LABEL: define {{.*}} @_ZN10paren_init8new_zeroILi0EEEvDTcmT_nw_NS_1AEpiEE
+  template void new_zero<0>(A *);
+  // CHECK-LABEL: define {{.*}} @_ZN10paren_init7new_oneILi0EEEvDTcmT_nw_NS_1AEpiLi1EEE
+  template void new_one<0>(A *);
+  // CHECK-LABEL: define {{.*}} @_ZN10paren_init7new_twoILi0EEEvDTcmT_nw_NS_1BEpiLi1ELi2EEE
+  template void new_two<0>(B *);
+}

>From 2b090ce3c374d4d00ab88dca43de31405bd3bbb9 Mon Sep 17 00:00:00 2001
From: Kunal Dubey <xakep8 at protonmail.com>
Date: Fri, 16 Oct 2026 11:37:51 +0530
Subject: [PATCH 2/2] [Clang] Avoid numbering lambdas with invalid types

During error recovery, a lambda call operator can contain an invalid parameter type. Lambda numbering attempts to mangle that type to build the lambda signature, which can reach an unreachable while processing the recovery expression.\n\nSkip lambda numbering when the call operator type contains errors so Clang reports the original diagnostic instead of crashing.
---
 clang/docs/ReleaseNotes.md            |  3 +++
 clang/lib/Sema/SemaLambda.cpp         |  3 +++
 clang/test/SemaCXX/crash-GH170185.cpp | 14 ++++++++++++++
 3 files changed, 20 insertions(+)
 create mode 100644 clang/test/SemaCXX/crash-GH170185.cpp

diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 0d5ef39c8d6bd7..3710ab8e36abb6 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -804,6 +804,9 @@ features cannot lower the translation-unit ABI level;
 
 #### Bug Fixes to C++ Support
 
+- Fixed a crash when a lambda parameter type contains an invalid non-type
+  template argument. (#GH170185)
+
 - Fixed lambdas with specifiers or attributes after the capture list being
   misparsed as function declarations in direct-initialization contexts under
   `-fms-extensions` or in HLSL mode.
diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp
index 0c9a92cdd0c320..ec74102c0ecaf7 100644
--- a/clang/lib/Sema/SemaLambda.cpp
+++ b/clang/lib/Sema/SemaLambda.cpp
@@ -489,6 +489,9 @@ bool Sema::DiagnoseInvalidExplicitObjectParameterInLambda(
 void Sema::handleLambdaNumbering(
     CXXRecordDecl *Class, CXXMethodDecl *Method,
     std::optional<CXXRecordDecl::LambdaNumbering> NumberingOverride) {
+  if (Method->getType()->containsErrors())
+    return;
+
   ContextRAII ManglingContext(*this, Class->getDeclContext());
 
   auto getMangleNumberingContext =
diff --git a/clang/test/SemaCXX/crash-GH170185.cpp b/clang/test/SemaCXX/crash-GH170185.cpp
new file mode 100644
index 00000000000000..21fb071a1e80fe
--- /dev/null
+++ b/clang/test/SemaCXX/crash-GH170185.cpp
@@ -0,0 +1,14 @@
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s
+
+struct Foo; // expected-note 2 {{forward declaration of 'Foo'}}
+
+template <Foo X> // expected-error {{non-type template parameter has incomplete type 'Foo'}}
+struct Arr;
+
+Foo f; // expected-error {{variable has incomplete type 'Foo'}}
+
+void foo() {
+  []() {
+    (void)[&](Arr<f>) {};
+  }();
+}



More information about the cfe-commits mailing list