[clang] [Clang] Handle CXXParenListInitExpr in BuildMemberInitializer (#213284, #189005) (PR #213565)
via cfe-commits
cfe-commits at lists.llvm.org
Sun Aug 2 11:45:58 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: babadany2999
<details>
<summary>Changes</summary>
### Summary
Fixes a miscompilation (#<!-- -->213284) and an assertion crash / ICE (#<!-- -->189005) when template instantiation performs C++20 parenthesized aggregate member initialization.
Fixes #<!-- -->213284
Fixes #<!-- -->189005
### Root Cause
When instantiating a template class constructor containing C++20 parenthesized aggregate member initializers (e.g., `Result() : thing(0) {}` or `S() : m({{1}, {2}}) {}`), template instantiation creates a `CXXParenListInitExpr` AST node for the initializer.
In `Sema::BuildMemberInitializer`, the `if-else` chain checked for `ParenListExpr` and `InitListExpr`, but omitted `CXXParenListInitExpr`. Consequently, Clang fell through to `else { Args = Init; }`, treating the entire `CXXParenListInitExpr` object as a single untyped initializer expression rather than extracting its inner member initializer expressions (`CXXList->getInitExprs()`).
This caused aggregate member matching during template instantiation to fail, leading to integer argument truncation (`store i32` instead of `store i64`) in #<!-- -->213284 and a segfault in `AggExprEmitter::VisitCXXParenListOrInitListExpr` in #<!-- -->189005.
### Solution
Added `else-if` block checking for `CXXParenListInitExpr` in `clang/lib/Sema/SemaDeclCXX.cpp` to extract inner initialization expressions.
### Test Plan
- Added two regression tests, one (`clang/test/CodeGen/gh189005.cpp`) fixing #<!-- -->189005, and another (`clang/test/CodeGen/gh213284.cpp`) fixing #<!-- -->213284.
- Verified tests pass cleanly across `clang/test/Sema` and `clang/test/CodeGen`.
- Added two release notes to `clang/docs/ReleaseNotes.md` under *Bug Fixes to C++ Support*.
### Notes
- I believe the testing suite `clang/test/CodeGen/gh213284.cpp` may be better suited to live in `clang/test/Sema`, but I am unsure.
---
Full diff: https://github.com/llvm/llvm-project/pull/213565.diff
4 Files Affected:
- (modified) clang/docs/ReleaseNotes.md (+6)
- (modified) clang/lib/Sema/SemaDeclCXX.cpp (+2)
- (added) clang/test/CodeGen/gh189005.cpp (+99)
- (added) clang/test/CodeGen/gh213284.cpp (+17)
``````````diff
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index a38b99ff8e075..ba95d6aff619b 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -424,6 +424,12 @@ features cannot lower the translation-unit ABI level;
copy so the union's object representation is copied, matching the defaulted
union copy constructor.
+- Fixed a miscompile where C++20 parenthesized aggregate initialization generated
+ invalid LLVM IR. (GH#213284)
+
+- Fixed a crash when compiling C++20 parenthesized aggregate initialization in
+ template constructor member initializers. (GH#189005)
+
#### 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..6202470cb13c6 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -4657,6 +4657,8 @@ Sema::BuildMemberInitializer(ValueDecl *Member, Expr *Init,
Args = MultiExprArg(ParenList->getExprs(), ParenList->getNumExprs());
} else if (InitListExpr *InitList = dyn_cast<InitListExpr>(Init)) {
Args = MultiExprArg(InitList->getInits(), InitList->getNumInits());
+ } else if (CXXParenListInitExpr *CXXInitList = dyn_cast<CXXParenListInitExpr>(Init)) {
+ Args = CXXInitList->getInitExprs();
} else {
// Template instantiation doesn't reconstruct ParenListExprs for us.
Args = Init;
diff --git a/clang/test/CodeGen/gh189005.cpp b/clang/test/CodeGen/gh189005.cpp
new file mode 100644
index 0000000000000..1252e803ad394
--- /dev/null
+++ b/clang/test/CodeGen/gh189005.cpp
@@ -0,0 +1,99 @@
+// RUN: %clang_cc1 -std=c++20 %s -emit-llvm -o -
+
+namespace std {
+
+template <typename T1, typename T2>
+struct pair {
+ T1 first;
+ T2 second;
+
+ // Constructor needed so this reproduces the std::array<std::pair>
+ // initialization from the original report.
+ constexpr
+ pair(const T1& a, const T2& b)
+ : first(a), second(b)
+ {}
+};
+
+template <typename T, unsigned long N>
+struct array {
+ T elems[N];
+};
+} // namespace std
+
+// Nested aggregate containing an array of aggregates.
+struct Inner {
+ int x;
+};
+
+struct Outer {
+ Inner arr[2];
+};
+
+template <typename T>
+struct S {
+ S() : m({{1}, {2}}) {}
+ Outer m;
+};
+
+template struct S<int>;
+
+// std::array<std::pair>-style initialization.
+template <typename T>
+struct S2 {
+ S2() : a({{1, 2}}) {}
+ std::array<std::pair<int, int>, 1> a;
+};
+
+template struct S2<int>;
+
+// Designated initializer.
+struct Point {
+ int x;
+ int y;
+};
+
+struct Config {
+ Point x;
+};
+
+template <typename T>
+struct Designated {
+ Designated() : cfg({.x = 10, .y = 5}) {}
+ Config cfg;
+};
+
+template struct Designated<int>;
+
+// String literal initializer.
+// Not affected by this fix, but kept as a regression
+// for another aggregate initialization path.
+struct Buffer {
+ char data[10];
+};
+
+template <typename T>
+struct String {
+ String() : buf("hello") {}
+ Buffer buf;
+};
+
+template struct String<int>;
+
+// Parenthesized aggregate with multiple arguments.
+struct First {
+ int x;
+};
+
+struct Second {
+ First a;
+ int y;
+};
+
+template <typename T>
+struct Nested {
+ Nested() : sec({1}, 2) {}
+ Second sec;
+};
+
+template struct Nested<int>;
diff --git a/clang/test/CodeGen/gh213284.cpp b/clang/test/CodeGen/gh213284.cpp
new file mode 100644
index 0000000000000..7e875bb9d616e
--- /dev/null
+++ b/clang/test/CodeGen/gh213284.cpp
@@ -0,0 +1,17 @@
+// RUN: %clang_cc1 -std=c++20 %s -emit-llvm -triple x86_64-unknown-linux-gnu -o - | FileCheck %s
+
+struct Ref {
+ unsigned long long bits;
+};
+
+template <typename>
+struct Result {
+ Result() : thing(0) {}
+ Ref thing;
+};
+
+Result<void> construct() {
+ return Result<void>();
+}
+// CHECK-LABEL: define {{.*}}construct
+// CHECK: store i64 0
``````````
</details>
https://github.com/llvm/llvm-project/pull/213565
More information about the cfe-commits
mailing list