Author: Akash Manna
Date: 2026-09-24T13:48:43-07:00
New Revision: d051060325e27aba3536419925f985321e441db9

URL: 
https://github.com/llvm/llvm-project/commit/d051060325e27aba3536419925f985321e441db9
DIFF: 
https://github.com/llvm/llvm-project/commit/d051060325e27aba3536419925f985321e441db9.diff

LOG: [Clang] Re-analyze parenthesized aggregate mem-initializers on 
instantiation (#224918)

Fixes #176161
Fixes #189005
Fixes #213284

Since #138518, template instantiation keeps a parenthesized aggregate
initialization as a `CXXParenListInitExpr`, but its elements have
already been reverted to their written form (a braced list stays a
braced list, an implicit cast is stripped), so whoever consumes the node
has to redo the initialization from them. Variable initializers,
new-expressions and functional casts do that. `BuildMemberInitializer`
didn't: it took the whole node as one argument, guaranteed elision
accepted it as-is, and the constructor ended up with a member
initializer that was never analyzed. Depending on the shape that's the
`EmitAggExpr` assertion (#176161, #189005), a `0` stored as 32 bits into
a 64-bit field (#213284), a rejected valid array member like `arr(1,
2)`, or a union member silently left uninitialized. Clang 20 handled all
of these. The lambda and local class in #176161 are incidental; a plain
class template reproduces it.

`BuildMemberInitializer` now treats a `CXXParenListInitExpr` the way it
treats a `ParenListExpr` and performs the initialization from the
user-specified elements. Using only those elements (not the trailing
default-member-initializer and value-initialization ones Sema appends)
means they get rebuilt for the instantiated class rather than fed back
in as arguments. This mirrors what #141342 did for variable initializers
and #142988 for new-expressions, and supersedes #213565.

Added: 
    clang/test/CodeGenCXX/GH176161.cpp

Modified: 
    clang/docs/ReleaseNotes.md
    clang/lib/Sema/SemaDeclCXX.cpp
    clang/test/SemaCXX/paren-list-agg-init.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 69206eb21ab2e..344cfebe16688 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -727,6 +727,10 @@ features cannot lower the translation-unit ABI level;
   inside a union caused the union to be treated as a polymorphic class.
   (#GH213854)
 
+- Fixed a crash, a miscompile and a rejected-valid case when instantiating a
+  constructor whose mem-initializer used parenthesized aggregate 
initialization,
+  e.g. ``: agg({1, 2})`` or ``: arr(1, 2)``. (#GH176161, #GH189005, #GH213284)
+
 - Fixed an assertion when a type-trait keyword that had already been made
   available as an identifier (e.g. `struct __make_unsigned`) was seen again
   in a token that was lexed and cached before the first occurrence was parsed.

diff  --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 3639640f2886a..68ad77ec5e8c7 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -4703,6 +4703,10 @@ 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 (auto *ParenListInit = dyn_cast<CXXParenListInitExpr>(Init)) {
+    // Template instantiation reverts the elements to their syntactic form;
+    // redo the initialization from the written arguments.
+    Args = ParenListInit->getUserSpecifiedInitExprs();
   } else {
     // Template instantiation doesn't reconstruct ParenListExprs for us.
     Args = Init;

diff  --git a/clang/test/CodeGenCXX/GH176161.cpp 
b/clang/test/CodeGenCXX/GH176161.cpp
new file mode 100644
index 0000000000000..d175b4d58dc24
--- /dev/null
+++ b/clang/test/CodeGenCXX/GH176161.cpp
@@ -0,0 +1,53 @@
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++23 -emit-llvm -o - %s | 
FileCheck %s
+
+template <typename T> struct ConfigFlag { T value; };
+
+struct Tuple {
+  Tuple(ConfigFlag<bool> &&a, ConfigFlag<bool> &&b) : first(a), second(b) {}
+  ConfigFlag<bool> first, second;
+};
+
+struct ConfigSystem {
+  Tuple flags;
+};
+
+template <unsigned R> class M {
+public:
+  M(const int *arr) {
+    auto flagProcessing = [] {
+      struct Configurator {
+        ConfigSystem config;
+        Configurator()
+            : config({ConfigFlag<bool>(true), ConfigFlag<bool>(false)}) {}
+      };
+      Configurator configurator;
+    };
+    flagProcessing();
+    for (unsigned long r = 0; r < R; ++r)
+      m[r] = arr[r];
+  }
+  int m[R];
+};
+
+int main() {
+  int arr[2] = {1, 2};
+  M<2> m(arr);
+}
+
+// CHECK-LABEL: define {{.*}}12ConfiguratorC2Ev(
+// CHECK: store i8 1, ptr
+// CHECK: store i8 0, ptr
+// CHECK: call void @_ZN5TupleC{{[12]}}E
+// CHECK: ret void
+
+namespace GH213284 {
+struct Ref { unsigned long long bits; };
+template <typename> struct Result {
+  Result() : thing(0) {}
+  Ref thing;
+};
+Result<void> construct() { return Result<void>(); }
+}
+
+// CHECK-LABEL: define {{.*}}@_ZN8GH2132846ResultIvEC2Ev(
+// CHECK: store i64 0, ptr

diff  --git a/clang/test/SemaCXX/paren-list-agg-init.cpp 
b/clang/test/SemaCXX/paren-list-agg-init.cpp
index 680fdcdbe7b1c..01e8e7638141f 100644
--- a/clang/test/SemaCXX/paren-list-agg-init.cpp
+++ b/clang/test/SemaCXX/paren-list-agg-init.cpp
@@ -425,3 +425,55 @@ void test() {
 }
 
 }
+
+namespace GH176161 {
+struct Inner {
+  constexpr Inner(int a, int b) : x(a), y(b) {}
+  int x, y;
+};
+struct Agg {
+  Inner i;
+  int k = 7;
+};
+
+template <class T> struct S {
+  Agg a;
+  constexpr S() : a({1, 2}) {} // beforecxx20-warning 2{{C++20 extension}}
+};
+
+constexpr S<int> s; // beforecxx20-note {{requested here}}
+static_assert(s.a.i.x == 1);
+static_assert(s.a.i.y == 2);
+static_assert(s.a.k == 7);
+
+struct X {
+  constexpr X() : v(5) {}
+  constexpr X(int v) : v(v) {}
+  int v;
+};
+template <class T> struct Arr {
+  X arr[3];
+  constexpr Arr() : arr(1, 2) {} // beforecxx20-warning 2{{C++20 extension}}
+};
+constexpr Arr<int> arr; // beforecxx20-note {{requested here}}
+static_assert(arr.arr[0].v == 1 && arr.arr[1].v == 2 && arr.arr[2].v == 5);
+
+union U { int a; float b; };
+template <class T> struct Un {
+  U u;
+  constexpr Un() : u('a') {} // beforecxx20-warning 2{{C++20 extension}}
+};
+constexpr Un<int> un; // beforecxx20-note {{requested here}}
+static_assert(un.u.a == 'a');
+}
+
+namespace GH189005 {
+struct Elem { int x; };
+struct Outer { Elem arr[2]; };
+template <class T> struct Nested {
+  Outer m;
+  constexpr Nested() : m({{1}, {2}}) {} // beforecxx20-warning 2{{C++20 
extension}}
+};
+constexpr Nested<int> n; // beforecxx20-note {{requested here}}
+static_assert(n.m.arr[0].x == 1 && n.m.arr[1].x == 2);
+}


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to