https://github.com/akash-manna-sky updated 
https://github.com/llvm/llvm-project/pull/218262

>From 615dfb8496b61dbdd80cd32254d46c320beaac52 Mon Sep 17 00:00:00 2001
From: Akash Manna <[email protected]>
Date: Sun, 23 Aug 2026 22:04:43 +0530
Subject: [PATCH 1/5] [clang][CodeGen] Fix assertion failure with #embed in
 array new-expression initializers

An EmbedExpr in a semantic initializer list can represent many array
elements, but EmitNewArrayInitializer counted it as one and emitted it
through the scalar path, hitting assert(E->getDataElementCount() == 1).
Emit one store per embed data element instead, count initializers with
getNumInitsWithEmbedExpanded() (also for the minimum-allocation check),
and make that helper look through implicit casts the way the other
embed consumers already do.

Fixes #128985
---
 clang/docs/ReleaseNotes.md         |  4 ++
 clang/include/clang/AST/Expr.h     |  2 +-
 clang/lib/CodeGen/CGExprCXX.cpp    | 34 +++++++++---
 clang/test/CodeGenCXX/GH128985.cpp | 89 ++++++++++++++++++++++++++++++
 4 files changed, 120 insertions(+), 9 deletions(-)
 create mode 100644 clang/test/CodeGenCXX/GH128985.cpp

diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 3c6694f510952..a973b143e6e2e 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -531,6 +531,10 @@ features cannot lower the translation-unit ABI level;
   parameter that follows a parameter pack (e.g.
   `template <typename... T> S::S(T..., int = 10) {}`).  (#GH216211)
 
+- Fixed an assertion failure when `#embed` was used in the braced initializer
+  of an array new-expression; codegen now expands the embedded data into the
+  individual array elements. (#GH128985)
+
 #### Bug Fixes to AST Handling
 
 - Fixed a non-deterministic ordering of unused local typedefs that made
diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h
index 72762c668f26a..93b5a8aea6614 100644
--- a/clang/include/clang/AST/Expr.h
+++ b/clang/include/clang/AST/Expr.h
@@ -5365,7 +5365,7 @@ class InitListExpr : public Expr {
   unsigned getNumInitsWithEmbedExpanded() const {
     unsigned Sum = InitExprs.size();
     for (auto *IE : InitExprs)
-      if (auto *EE = dyn_cast<EmbedExpr>(IE))
+      if (auto *EE = 
dyn_cast<EmbedExpr>(cast<Expr>(IE)->IgnoreParenImpCasts()))
         Sum += EE->getDataElementCount() - 1;
     return Sum;
   }
diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp
index e400a5c5a49c5..39c0e94b488f4 100644
--- a/clang/lib/CodeGen/CGExprCXX.cpp
+++ b/clang/lib/CodeGen/CGExprCXX.cpp
@@ -1103,7 +1103,8 @@ void CodeGenFunction::EmitNewArrayInitializer(
 
     ArrayRef<const Expr *> InitExprs =
         ILE ? ILE->inits() : CPLIE->getInitExprs();
-    InitListElements = InitExprs.size();
+    InitListElements =
+        ILE ? ILE->getNumInitsWithEmbedExpanded() : InitExprs.size();
 
     // If this is a multi-dimensional array new, we will initialize multiple
     // elements with each init list element.
@@ -1138,6 +1139,14 @@ void CodeGenFunction::EmitNewArrayInitializer(
 
     CharUnits StartAlign = CurPtr.getAlignment();
     unsigned i = 0;
+    auto AdvanceToNextElement = [&]() {
+      CurPtr = Address(Builder.CreateInBoundsGEP(CurPtr.getElementType(),
+                                                 CurPtr.emitRawPointer(*this),
+                                                 Builder.getSize(1),
+                                                 "array.exp.next"),
+                       CurPtr.getElementType(),
+                       StartAlign.alignmentAtOffset((++i) * ElementSize));
+    };
     for (const Expr *IE : InitExprs) {
       // Tell the cleanup that it needs to destroy up to this
       // element.  TODO: some of these stores can be trivially
@@ -1145,17 +1154,25 @@ void CodeGenFunction::EmitNewArrayInitializer(
       if (EndOfInit.isValid()) {
         Builder.CreateStore(CurPtr.emitRawPointer(*this), EndOfInit);
       }
+      // An EmbedExpr can initialize more than one array element.
+      if (const auto *EmbedS = dyn_cast<EmbedExpr>(IE->IgnoreParenImpCasts())) 
{
+        for (const IntegerLiteral *DataElement :
+             EmbedS->underlying_data_elements()) {
+          llvm::Value *Val = EmitScalarConversion(
+              Builder.getInt(DataElement->getValue()), DataElement->getType(),
+              ElementType, DataElement->getExprLoc());
+          EmitStoreOfScalar(Val, MakeAddrLValue(CurPtr, ElementType),
+                            /*isInit=*/true);
+          AdvanceToNextElement();
+        }
+        continue;
+      }
       // FIXME: If the last initializer is an incomplete initializer list for
       // an array, and we have an array filler, we can fold together the two
       // initialization loops.
       StoreAnyExprIntoOneUnit(*this, IE, IE->getType(), CurPtr,
                               AggValueSlot::DoesNotOverlap);
-      CurPtr = Address(Builder.CreateInBoundsGEP(CurPtr.getElementType(),
-                                                 CurPtr.emitRawPointer(*this),
-                                                 Builder.getSize(1),
-                                                 "array.exp.next"),
-                       CurPtr.getElementType(),
-                       StartAlign.alignmentAtOffset((++i) * ElementSize));
+      AdvanceToNextElement();
     }
 
     // The remaining elements are filled with the array filler expression.
@@ -1591,7 +1608,8 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const 
CXXNewExpr *E) {
           cast<ConstantArrayType>(Init->getType()->getAsArrayTypeUnsafe())
               ->getZExtSize();
     } else if (ILE || CPLIE) {
-      minElements = ILE ? ILE->getNumInits() : CPLIE->getInitExprs().size();
+      minElements = ILE ? ILE->getNumInitsWithEmbedExpanded()
+                        : CPLIE->getInitExprs().size();
     }
   }
 
diff --git a/clang/test/CodeGenCXX/GH128985.cpp 
b/clang/test/CodeGenCXX/GH128985.cpp
new file mode 100644
index 0000000000000..7b703af005687
--- /dev/null
+++ b/clang/test/CodeGenCXX/GH128985.cpp
@@ -0,0 +1,89 @@
+// RUN: %clang_cc1 %s -triple x86_64 -emit-llvm -o - | FileCheck %s
+
+// GH128985: #embed in the braced initializer of an array new-expression
+// asserted in codegen.
+// The first four bytes of this file are '/', '/', ' ', 'R' (47, 47, 32, 82).
+
+// CHECK-LABEL: define {{.*}}void @_Z2f1i(
+// CHECK: icmp ult i64 %{{.*}}, 4
+// CHECK: %[[A1:.*]] = call {{.*}}ptr @_Znam(i64 {{.*}})
+// CHECK: store i32 47, ptr %[[A1]]
+// CHECK: %[[F1E1:.*]] = getelementptr inbounds i32, ptr %[[A1]], i64 1
+// CHECK: store i32 47, ptr %[[F1E1]]
+// CHECK: %[[F1E2:.*]] = getelementptr inbounds i32, ptr %[[F1E1]], i64 1
+// CHECK: store i32 32, ptr %[[F1E2]]
+// CHECK: %[[F1E3:.*]] = getelementptr inbounds i32, ptr %[[F1E2]], i64 1
+// CHECK: store i32 82, ptr %[[F1E3]]
+// CHECK: %[[F1REST:.*]] = sub i64 %{{.*}}, 16
+// CHECK: call void @llvm.memset.p0.i64(ptr align 4 %{{.*}}, i8 0, i64 
%[[F1REST]], i1 false)
+void f1(int x) {
+  int *p = new int[x]{
+#embed __FILE__ limit(4)
+  };
+}
+
+// CHECK-LABEL: define {{.*}}void @_Z2f2i(
+// CHECK: icmp ult i64 %{{.*}}, 4
+// CHECK: %[[A2:.*]] = call {{.*}}ptr @_Znam(i64 {{.*}})
+// CHECK: store i32 500, ptr %[[A2]]
+// CHECK: %[[F2E1:.*]] = getelementptr inbounds i32, ptr %[[A2]], i64 1
+// CHECK: store i32 47, ptr %[[F2E1]]
+// CHECK: %[[F2E2:.*]] = getelementptr inbounds i32, ptr %[[F2E1]], i64 1
+// CHECK: store i32 47, ptr %[[F2E2]]
+// CHECK: %[[F2E3:.*]] = getelementptr inbounds i32, ptr %[[F2E2]], i64 1
+// CHECK: store i32 600, ptr %[[F2E3]]
+// CHECK: %[[F2REST:.*]] = sub i64 %{{.*}}, 16
+// CHECK: call void @llvm.memset.p0.i64(ptr align 4 %{{.*}}, i8 0, i64 
%[[F2REST]], i1 false)
+void f2(int x) {
+  int *p = new int[x]{
+    500,
+#embed __FILE__ limit(2) suffix(, 600)
+  };
+}
+
+// char arrays are initialized from the embed data via the string literal
+// initialization path.
+// CHECK-LABEL: define {{.*}}void @_Z2f3i(
+// CHECK: icmp ult i64 %{{.*}}, 4
+// CHECK: %[[A3:.*]] = call {{.*}}ptr @_Znam(i64 {{.*}})
+// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 1 %[[A3]], ptr align 1 
@{{.*}}, i64 4, i1 false)
+// CHECK: %[[F3END:.*]] = getelementptr inbounds i8, ptr %[[A3]], i64 4
+// CHECK: %[[F3REST:.*]] = sub i64 %{{.*}}, 4
+// CHECK: call void @llvm.memset.p0.i64(ptr align 1 %[[F3END]], i8 0, i64 
%[[F3REST]], i1 false)
+void f3(int x) {
+  char *p = new char[x]{
+#embed __FILE__ limit(4)
+  };
+}
+
+// CHECK-LABEL: define {{.*}}void @_Z2f4i(
+// CHECK: icmp ult i64 %{{.*}}, 2
+// CHECK: %[[A4:.*]] = call {{.*}}ptr @_Znam(i64 {{.*}})
+// CHECK: store i32 900, ptr %[[A4]]
+// CHECK: %[[F4E1:.*]] = getelementptr inbounds i32, ptr %[[A4]], i64 1
+// CHECK: store i32 47, ptr %[[F4E1]]
+// CHECK: %[[F4REST:.*]] = sub i64 %{{.*}}, 8
+// CHECK: call void @llvm.memset.p0.i64(ptr align 4 %{{.*}}, i8 0, i64 
%[[F4REST]], i1 false)
+void f4(int x) {
+  int *p = new int[x]{
+#embed __FILE__ limit(1) prefix(900, )
+  };
+}
+
+// Constant size fully covered by the embed data: no trailing fill.
+// CHECK-LABEL: define {{.*}}void @_Z2f5v(
+// CHECK: %[[A5:.*]] = call {{.*}}ptr @_Znam(i64 {{.*}})
+// CHECK: store i32 47, ptr %[[A5]]
+// CHECK: %[[F5E1:.*]] = getelementptr inbounds i32, ptr %[[A5]], i64 1
+// CHECK: store i32 47, ptr %[[F5E1]]
+// CHECK: %[[F5E2:.*]] = getelementptr inbounds i32, ptr %[[F5E1]], i64 1
+// CHECK: store i32 32, ptr %[[F5E2]]
+// CHECK: %[[F5E3:.*]] = getelementptr inbounds i32, ptr %[[F5E2]], i64 1
+// CHECK: store i32 82, ptr %[[F5E3]]
+// CHECK-NOT: call void @llvm.memset
+// CHECK: ret void
+void f5() {
+  int *p = new int[4]{
+#embed __FILE__ limit(4)
+  };
+}

>From df02caddd25573b2ce95f071453d00069c096607 Mon Sep 17 00:00:00 2001
From: Akash Manna <[email protected]>
Date: Mon, 24 Aug 2026 23:31:48 +0530
Subject: [PATCH 2/5] [clang][CodeGen] Address review comments for #embed in
 array new

Use a dedicated input file instead of __FILE__, add codegen coverage for
non-int element types, multidimensional arrays, arrays of structs and
deduced array bounds, and add a Sema test for too many/too few embed
elements with a constant bound.
---
 clang/test/CodeGenCXX/GH128985.cpp          | 130 ++++++++++++++++----
 clang/test/CodeGenCXX/Inputs/embed-data.txt |   1 +
 clang/test/SemaCXX/GH128985.cpp             |  38 ++++++
 3 files changed, 147 insertions(+), 22 deletions(-)
 create mode 100644 clang/test/CodeGenCXX/Inputs/embed-data.txt
 create mode 100644 clang/test/SemaCXX/GH128985.cpp

diff --git a/clang/test/CodeGenCXX/GH128985.cpp 
b/clang/test/CodeGenCXX/GH128985.cpp
index 7b703af005687..87ea50661b09c 100644
--- a/clang/test/CodeGenCXX/GH128985.cpp
+++ b/clang/test/CodeGenCXX/GH128985.cpp
@@ -1,24 +1,26 @@
-// RUN: %clang_cc1 %s -triple x86_64 -emit-llvm -o - | FileCheck %s
+// RUN: %clang_cc1 %s -triple x86_64 --embed-dir=%S/Inputs -emit-llvm -o - | 
FileCheck %s
 
-// GH128985: #embed in the braced initializer of an array new-expression
-// asserted in codegen.
-// The first four bytes of this file are '/', '/', ' ', 'R' (47, 47, 32, 82).
+// embed-data.txt contains "0123456789" (48 ... 57) without a trailing newline.
+
+struct S {
+  int a, b;
+};
 
 // CHECK-LABEL: define {{.*}}void @_Z2f1i(
 // CHECK: icmp ult i64 %{{.*}}, 4
 // CHECK: %[[A1:.*]] = call {{.*}}ptr @_Znam(i64 {{.*}})
-// CHECK: store i32 47, ptr %[[A1]]
+// CHECK: store i32 48, ptr %[[A1]]
 // CHECK: %[[F1E1:.*]] = getelementptr inbounds i32, ptr %[[A1]], i64 1
-// CHECK: store i32 47, ptr %[[F1E1]]
+// CHECK: store i32 49, ptr %[[F1E1]]
 // CHECK: %[[F1E2:.*]] = getelementptr inbounds i32, ptr %[[F1E1]], i64 1
-// CHECK: store i32 32, ptr %[[F1E2]]
+// CHECK: store i32 50, ptr %[[F1E2]]
 // CHECK: %[[F1E3:.*]] = getelementptr inbounds i32, ptr %[[F1E2]], i64 1
-// CHECK: store i32 82, ptr %[[F1E3]]
+// CHECK: store i32 51, ptr %[[F1E3]]
 // CHECK: %[[F1REST:.*]] = sub i64 %{{.*}}, 16
 // CHECK: call void @llvm.memset.p0.i64(ptr align 4 %{{.*}}, i8 0, i64 
%[[F1REST]], i1 false)
 void f1(int x) {
   int *p = new int[x]{
-#embed __FILE__ limit(4)
+#embed <embed-data.txt> limit(4)
   };
 }
 
@@ -27,9 +29,9 @@ void f1(int x) {
 // CHECK: %[[A2:.*]] = call {{.*}}ptr @_Znam(i64 {{.*}})
 // CHECK: store i32 500, ptr %[[A2]]
 // CHECK: %[[F2E1:.*]] = getelementptr inbounds i32, ptr %[[A2]], i64 1
-// CHECK: store i32 47, ptr %[[F2E1]]
+// CHECK: store i32 48, ptr %[[F2E1]]
 // CHECK: %[[F2E2:.*]] = getelementptr inbounds i32, ptr %[[F2E1]], i64 1
-// CHECK: store i32 47, ptr %[[F2E2]]
+// CHECK: store i32 49, ptr %[[F2E2]]
 // CHECK: %[[F2E3:.*]] = getelementptr inbounds i32, ptr %[[F2E2]], i64 1
 // CHECK: store i32 600, ptr %[[F2E3]]
 // CHECK: %[[F2REST:.*]] = sub i64 %{{.*}}, 16
@@ -37,12 +39,11 @@ void f1(int x) {
 void f2(int x) {
   int *p = new int[x]{
     500,
-#embed __FILE__ limit(2) suffix(, 600)
+#embed <embed-data.txt> limit(2) suffix(, 600)
   };
 }
 
-// char arrays are initialized from the embed data via the string literal
-// initialization path.
+// char arrays go through the string literal initialization path.
 // CHECK-LABEL: define {{.*}}void @_Z2f3i(
 // CHECK: icmp ult i64 %{{.*}}, 4
 // CHECK: %[[A3:.*]] = call {{.*}}ptr @_Znam(i64 {{.*}})
@@ -52,7 +53,7 @@ void f2(int x) {
 // CHECK: call void @llvm.memset.p0.i64(ptr align 1 %[[F3END]], i8 0, i64 
%[[F3REST]], i1 false)
 void f3(int x) {
   char *p = new char[x]{
-#embed __FILE__ limit(4)
+#embed <embed-data.txt> limit(4)
   };
 }
 
@@ -61,29 +62,114 @@ void f3(int x) {
 // CHECK: %[[A4:.*]] = call {{.*}}ptr @_Znam(i64 {{.*}})
 // CHECK: store i32 900, ptr %[[A4]]
 // CHECK: %[[F4E1:.*]] = getelementptr inbounds i32, ptr %[[A4]], i64 1
-// CHECK: store i32 47, ptr %[[F4E1]]
+// CHECK: store i32 48, ptr %[[F4E1]]
 // CHECK: %[[F4REST:.*]] = sub i64 %{{.*}}, 8
 // CHECK: call void @llvm.memset.p0.i64(ptr align 4 %{{.*}}, i8 0, i64 
%[[F4REST]], i1 false)
 void f4(int x) {
   int *p = new int[x]{
-#embed __FILE__ limit(1) prefix(900, )
+#embed <embed-data.txt> limit(1) prefix(900, )
   };
 }
 
 // Constant size fully covered by the embed data: no trailing fill.
 // CHECK-LABEL: define {{.*}}void @_Z2f5v(
 // CHECK: %[[A5:.*]] = call {{.*}}ptr @_Znam(i64 {{.*}})
-// CHECK: store i32 47, ptr %[[A5]]
+// CHECK: store i32 48, ptr %[[A5]]
 // CHECK: %[[F5E1:.*]] = getelementptr inbounds i32, ptr %[[A5]], i64 1
-// CHECK: store i32 47, ptr %[[F5E1]]
+// CHECK: store i32 49, ptr %[[F5E1]]
 // CHECK: %[[F5E2:.*]] = getelementptr inbounds i32, ptr %[[F5E1]], i64 1
-// CHECK: store i32 32, ptr %[[F5E2]]
+// CHECK: store i32 50, ptr %[[F5E2]]
 // CHECK: %[[F5E3:.*]] = getelementptr inbounds i32, ptr %[[F5E2]], i64 1
-// CHECK: store i32 82, ptr %[[F5E3]]
+// CHECK: store i32 51, ptr %[[F5E3]]
 // CHECK-NOT: call void @llvm.memset
 // CHECK: ret void
 void f5() {
   int *p = new int[4]{
-#embed __FILE__ limit(4)
+#embed <embed-data.txt> limit(4)
+  };
+}
+
+// Sema wraps the EmbedExpr in an implicit conversion to the element type.
+// CHECK-LABEL: define {{.*}}void @_Z2f6i(
+// CHECK: icmp ult i64 %{{.*}}, 4
+// CHECK: %[[A6:.*]] = call {{.*}}ptr @_Znam(i64 {{.*}})
+// CHECK: store i64 48, ptr %[[A6]]
+// CHECK: %[[F6E1:.*]] = getelementptr inbounds i64, ptr %[[A6]], i64 1
+// CHECK: store i64 49, ptr %[[F6E1]]
+// CHECK: %[[F6E2:.*]] = getelementptr inbounds i64, ptr %[[F6E1]], i64 1
+// CHECK: store i64 50, ptr %[[F6E2]]
+// CHECK: %[[F6E3:.*]] = getelementptr inbounds i64, ptr %[[F6E2]], i64 1
+// CHECK: store i64 51, ptr %[[F6E3]]
+// CHECK: %[[F6REST:.*]] = sub i64 %{{.*}}, 32
+// CHECK: call void @llvm.memset.p0.i64(ptr align 8 %{{.*}}, i8 0, i64 
%[[F6REST]], i1 false)
+void f6(int x) {
+  long long *p = new long long[x]{
+#embed <embed-data.txt> limit(4)
+  };
+}
+
+// CHECK-LABEL: define {{.*}}void @_Z2f7i(
+// CHECK: icmp ult i64 %{{.*}}, 2
+// CHECK: call {{.*}}ptr @_Znam(i64 {{.*}})
+// CHECK: store i32 48, ptr
+// CHECK: store i32 49, ptr
+// CHECK: store i32 50, ptr
+// CHECK: store i32 51, ptr
+// CHECK: %[[F7REST:.*]] = sub i64 %{{.*}}, 16
+// CHECK: call void @llvm.memset.p0.i64(ptr align {{[0-9]+}} %{{.*}}, i8 0, 
i64 %[[F7REST]], i1 false)
+void f7(int x) {
+  int (*p)[2] = new int[x][2]{
+#embed <embed-data.txt> limit(4)
+  };
+}
+
+// CHECK-LABEL: define {{.*}}void @_Z2f8i(
+// CHECK: icmp ult i64 %{{.*}}, 2
+// CHECK: call {{.*}}ptr @_Znam(i64 {{.*}})
+// CHECK: store i32 48, ptr
+// CHECK: store i32 49, ptr
+// CHECK: store i32 50, ptr
+// CHECK: store i32 51, ptr
+// CHECK: %[[F8REST:.*]] = sub i64 %{{.*}}, 16
+// CHECK: call void @llvm.memset.p0.i64(ptr align {{[0-9]+}} %{{.*}}, i8 0, 
i64 %[[F8REST]], i1 false)
+void f8(int x) {
+  S *p = new S[x]{
+#embed <embed-data.txt> limit(4)
+  };
+}
+
+// CHECK-LABEL: define {{.*}}void @_Z2f9i(
+// CHECK: icmp ult i64 %{{.*}}, 2
+// CHECK: call {{.*}}ptr @_Znam(i64 {{.*}})
+// CHECK: store i32 48, ptr
+// CHECK: store i32 49, ptr
+// CHECK: store i32 50, ptr
+// CHECK: store i32 51, ptr
+// CHECK: store i32 52, ptr
+// CHECK: store i32 53, ptr
+// CHECK: store i32 54, ptr
+// CHECK: store i32 55, ptr
+// CHECK: %[[F9REST:.*]] = sub i64 %{{.*}}, 32
+// CHECK: call void @llvm.memset.p0.i64(ptr align {{[0-9]+}} %{{.*}}, i8 0, 
i64 %[[F9REST]], i1 false)
+void f9(int x) {
+  S (*p)[2] = new S[x][2]{
+#embed <embed-data.txt> limit(8)
+  };
+}
+
+// CHECK-LABEL: define {{.*}}void @_Z3f10v(
+// CHECK: %[[A10:.*]] = call {{.*}}ptr @_Znam(i64 noundef 16)
+// CHECK: store i32 48, ptr %[[A10]]
+// CHECK: %[[F10E1:.*]] = getelementptr inbounds i32, ptr %[[A10]], i64 1
+// CHECK: store i32 49, ptr %[[F10E1]]
+// CHECK: %[[F10E2:.*]] = getelementptr inbounds i32, ptr %[[F10E1]], i64 1
+// CHECK: store i32 50, ptr %[[F10E2]]
+// CHECK: %[[F10E3:.*]] = getelementptr inbounds i32, ptr %[[F10E2]], i64 1
+// CHECK: store i32 51, ptr %[[F10E3]]
+// CHECK-NOT: call void @llvm.memset
+// CHECK: ret void
+void f10() {
+  int *p = new int[]{
+#embed <embed-data.txt> limit(4)
   };
 }
diff --git a/clang/test/CodeGenCXX/Inputs/embed-data.txt 
b/clang/test/CodeGenCXX/Inputs/embed-data.txt
new file mode 100644
index 0000000000000..ad471007bd7f5
--- /dev/null
+++ b/clang/test/CodeGenCXX/Inputs/embed-data.txt
@@ -0,0 +1 @@
+0123456789
\ No newline at end of file
diff --git a/clang/test/SemaCXX/GH128985.cpp b/clang/test/SemaCXX/GH128985.cpp
new file mode 100644
index 0000000000000..adb0d8a1fc93f
--- /dev/null
+++ b/clang/test/SemaCXX/GH128985.cpp
@@ -0,0 +1,38 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -Wno-c23-extensions %s
+
+struct S {
+  int a, b;
+};
+
+void f(int x) {
+  int *a = new int[2]{
+#embed __FILE__ limit(4)
+  // expected-error@-1 {{excess elements in array initializer}}
+  };
+
+  int *b = new int[4]{
+#embed __FILE__ limit(4)
+  };
+
+  int *c = new int[8]{
+#embed __FILE__ limit(4)
+  };
+
+  int *d = new int[x]{
+#embed __FILE__ limit(4)
+  };
+
+  int (*e)[2] = new int[2][2]{
+#embed __FILE__ limit(5)
+  // expected-error@-1 {{excess elements in array initializer}}
+  };
+
+  S *s = new S[1]{
+#embed __FILE__ limit(3)
+  // expected-error@-1 {{excess elements in array initializer}}
+  };
+
+  S *t = new S[x]{
+#embed __FILE__ limit(3)
+  };
+}

>From 6dbf0263885b2814f41d8699e95a3c97604ffbe6 Mon Sep 17 00:00:00 2001
From: Akash Manna <[email protected]>
Date: Tue, 25 Aug 2026 21:35:55 +0530
Subject: [PATCH 3/5] [clang][CodeGen] Read embed data directly when
 initializing array new

Emit the elements of an EmbedExpr from its underlying string literal
instead of iterating the fake integer literals, and add a float array
test.
---
 clang/lib/CodeGen/CGExprCXX.cpp    | 11 +++++++----
 clang/test/CodeGenCXX/GH128985.cpp | 18 ++++++++++++++++++
 2 files changed, 25 insertions(+), 4 deletions(-)

diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp
index 39c0e94b488f4..3c141990dc887 100644
--- a/clang/lib/CodeGen/CGExprCXX.cpp
+++ b/clang/lib/CodeGen/CGExprCXX.cpp
@@ -1156,11 +1156,14 @@ void CodeGenFunction::EmitNewArrayInitializer(
       }
       // An EmbedExpr can initialize more than one array element.
       if (const auto *EmbedS = dyn_cast<EmbedExpr>(IE->IgnoreParenImpCasts())) 
{
-        for (const IntegerLiteral *DataElement :
-             EmbedS->underlying_data_elements()) {
+        const StringLiteral *SL = EmbedS->getDataStringLiteral();
+        llvm::Type *DataTy = ConvertType(EmbedS->getType());
+        for (unsigned I = EmbedS->getStartingElementPos(),
+                      End = I + EmbedS->getDataElementCount();
+             I != End; ++I) {
           llvm::Value *Val = EmitScalarConversion(
-              Builder.getInt(DataElement->getValue()), DataElement->getType(),
-              ElementType, DataElement->getExprLoc());
+              llvm::ConstantInt::get(DataTy, SL->getCodeUnit(I)),
+              EmbedS->getType(), ElementType, EmbedS->getLocation());
           EmitStoreOfScalar(Val, MakeAddrLValue(CurPtr, ElementType),
                             /*isInit=*/true);
           AdvanceToNextElement();
diff --git a/clang/test/CodeGenCXX/GH128985.cpp 
b/clang/test/CodeGenCXX/GH128985.cpp
index 87ea50661b09c..37660d47b29b8 100644
--- a/clang/test/CodeGenCXX/GH128985.cpp
+++ b/clang/test/CodeGenCXX/GH128985.cpp
@@ -173,3 +173,21 @@ void f10() {
 #embed <embed-data.txt> limit(4)
   };
 }
+
+// CHECK-LABEL: define {{.*}}void @_Z3f11i(
+// CHECK: icmp ult i64 %{{.*}}, 4
+// CHECK: %[[A11:.*]] = call {{.*}}ptr @_Znam(i64 {{.*}})
+// CHECK: store float 4.800000e+01, ptr %[[A11]]
+// CHECK: %[[F11E1:.*]] = getelementptr inbounds float, ptr %[[A11]], i64 1
+// CHECK: store float 4.900000e+01, ptr %[[F11E1]]
+// CHECK: %[[F11E2:.*]] = getelementptr inbounds float, ptr %[[F11E1]], i64 1
+// CHECK: store float 5.000000e+01, ptr %[[F11E2]]
+// CHECK: %[[F11E3:.*]] = getelementptr inbounds float, ptr %[[F11E2]], i64 1
+// CHECK: store float 5.100000e+01, ptr %[[F11E3]]
+// CHECK: %[[F11REST:.*]] = sub i64 %{{.*}}, 16
+// CHECK: call void @llvm.memset.p0.i64(ptr align 4 %{{.*}}, i8 0, i64 
%[[F11REST]], i1 false)
+void f11(int x) {
+  float *p = new float[x]{
+#embed <embed-data.txt> limit(4)
+  };
+}

>From 606fdb8a57dc7e13b1afdfad046dabdeb606a15d Mon Sep 17 00:00:00 2001
From: Akash Manna <[email protected]>
Date: Tue, 25 Aug 2026 22:31:02 +0530
Subject: [PATCH 4/5] [clang][Sema] Only expand #embed over multiple elements
 for scalar array elements

HandleEmbed returned a multi-element EmbedExpr for any array element,
so for a class element with a converting constructor the whole embed
became a single constructor argument. Restrict it to scalar element
types so class elements are constructed from one data element each.
---
 clang/lib/Sema/SemaInit.cpp        |  3 ++-
 clang/test/CodeGenCXX/GH128985.cpp | 16 ++++++++++++++++
 clang/test/SemaCXX/GH128985.cpp    | 12 ++++++++++++
 3 files changed, 30 insertions(+), 1 deletion(-)

diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 9b10ac1735c81..ef259bbb0e98c 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -563,7 +563,8 @@ class InitListChecker {
     // Reference just one if we're initializing a single scalar.
     uint64_t ElsCount = 1;
     // Otherwise try to fill whole array with embed data.
-    if (Entity.getKind() == InitializedEntity::EK_ArrayElement) {
+    if (Entity.getKind() == InitializedEntity::EK_ArrayElement &&
+        Entity.getType()->isScalarType()) {
       unsigned ArrIndex = Entity.getElementIndex();
       auto *AType =
           SemaRef.Context.getAsArrayType(Entity.getParent()->getType());
diff --git a/clang/test/CodeGenCXX/GH128985.cpp 
b/clang/test/CodeGenCXX/GH128985.cpp
index 37660d47b29b8..741d1f7ea1d80 100644
--- a/clang/test/CodeGenCXX/GH128985.cpp
+++ b/clang/test/CodeGenCXX/GH128985.cpp
@@ -6,6 +6,10 @@ struct S {
   int a, b;
 };
 
+struct A {
+  A(char);
+};
+
 // CHECK-LABEL: define {{.*}}void @_Z2f1i(
 // CHECK: icmp ult i64 %{{.*}}, 4
 // CHECK: %[[A1:.*]] = call {{.*}}ptr @_Znam(i64 {{.*}})
@@ -191,3 +195,15 @@ void f11(int x) {
 #embed <embed-data.txt> limit(4)
   };
 }
+
+// Class elements are constructed from one data element each.
+// CHECK-LABEL: define {{.*}}void @_Z3f12v(
+// CHECK: %[[A12:.*]] = call {{.*}}ptr @_Znam(i64 noundef 2)
+// CHECK: call void @_ZN1AC1Ec(ptr {{.*}}%[[A12]], i8 noundef signext 48)
+// CHECK: %[[F12E1:.*]] = getelementptr inbounds %struct.A, ptr %[[A12]], i64 1
+// CHECK: call void @_ZN1AC1Ec(ptr {{.*}}%[[F12E1]], i8 noundef signext 49)
+void f12() {
+  A *p = new A[]{
+#embed <embed-data.txt> limit(2)
+  };
+}
diff --git a/clang/test/SemaCXX/GH128985.cpp b/clang/test/SemaCXX/GH128985.cpp
index adb0d8a1fc93f..faa6eeab3a253 100644
--- a/clang/test/SemaCXX/GH128985.cpp
+++ b/clang/test/SemaCXX/GH128985.cpp
@@ -4,6 +4,10 @@ struct S {
   int a, b;
 };
 
+struct A {
+  A(char);
+};
+
 void f(int x) {
   int *a = new int[2]{
 #embed __FILE__ limit(4)
@@ -35,4 +39,12 @@ void f(int x) {
   S *t = new S[x]{
 #embed __FILE__ limit(3)
   };
+
+  A *u = new A[]{1, 2, 3,
+#embed __FILE__ limit(10)
+  };
+
+  A v[] = {1, 2, 3,
+#embed __FILE__ limit(10)
+  };
 }

>From f391dc9cd3fffcf9e834f4f63724640e245ed688 Mon Sep 17 00:00:00 2001
From: Akash Manna <[email protected]>
Date: Tue, 25 Aug 2026 23:28:33 +0530
Subject: [PATCH 5/5] [clang][Sema] Narrow multi-element #embed expansion to
 integer and floating-point arrays

Match the element types the constant evaluator and constant emitter
handle, and only take the multi-element path in array new codegen when
the embed actually covers more than one element, so single-element
embeds under other conversions (e.g. to _Complex) go through the normal
initializer path.
---
 clang/docs/ReleaseNotes.md         |  4 ++--
 clang/include/clang/AST/Expr.h     |  2 +-
 clang/lib/CodeGen/CGExprCXX.cpp    |  3 ++-
 clang/lib/Sema/SemaInit.cpp        |  3 ++-
 clang/test/CodeGenCXX/GH128985.cpp | 13 +++++++++++++
 clang/test/SemaCXX/GH128985.cpp    |  4 ++++
 6 files changed, 24 insertions(+), 5 deletions(-)

diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index a973b143e6e2e..c719a8c86d0cd 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -532,8 +532,8 @@ features cannot lower the translation-unit ABI level;
   `template <typename... T> S::S(T..., int = 10) {}`).  (#GH216211)
 
 - Fixed an assertion failure when `#embed` was used in the braced initializer
-  of an array new-expression; codegen now expands the embedded data into the
-  individual array elements. (#GH128985)
+  of an array new-expression, or of an array whose elements are of class type.
+  (#GH128985)
 
 #### Bug Fixes to AST Handling
 
diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h
index 93b5a8aea6614..737ec4134a449 100644
--- a/clang/include/clang/AST/Expr.h
+++ b/clang/include/clang/AST/Expr.h
@@ -5151,7 +5151,7 @@ struct EmbedDataStorage {
 ///  { {EE(9th and 10th element), { zeroinitializer }}}
 ///
 /// EmbedExpr inside of a semantic initializer list and referencing more than
-/// one element can only appear for arrays of scalars.
+/// one element can only appear for arrays of integer or floating-point type.
 class EmbedExpr final : public Expr {
   SourceLocation EmbedKeywordLoc;
   IntegerLiteral *FakeChildNode = nullptr;
diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp
index 3c141990dc887..7969aec916122 100644
--- a/clang/lib/CodeGen/CGExprCXX.cpp
+++ b/clang/lib/CodeGen/CGExprCXX.cpp
@@ -1155,7 +1155,8 @@ void CodeGenFunction::EmitNewArrayInitializer(
         Builder.CreateStore(CurPtr.emitRawPointer(*this), EndOfInit);
       }
       // An EmbedExpr can initialize more than one array element.
-      if (const auto *EmbedS = dyn_cast<EmbedExpr>(IE->IgnoreParenImpCasts())) 
{
+      const auto *EmbedS = dyn_cast<EmbedExpr>(IE->IgnoreParenImpCasts());
+      if (EmbedS && EmbedS->getDataElementCount() > 1) {
         const StringLiteral *SL = EmbedS->getDataStringLiteral();
         llvm::Type *DataTy = ConvertType(EmbedS->getType());
         for (unsigned I = EmbedS->getStartingElementPos(),
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index ef259bbb0e98c..086aed2e30d85 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -564,7 +564,8 @@ class InitListChecker {
     uint64_t ElsCount = 1;
     // Otherwise try to fill whole array with embed data.
     if (Entity.getKind() == InitializedEntity::EK_ArrayElement &&
-        Entity.getType()->isScalarType()) {
+        (Entity.getType()->isIntegerType() ||
+         Entity.getType()->isRealFloatingType())) {
       unsigned ArrIndex = Entity.getElementIndex();
       auto *AType =
           SemaRef.Context.getAsArrayType(Entity.getParent()->getType());
diff --git a/clang/test/CodeGenCXX/GH128985.cpp 
b/clang/test/CodeGenCXX/GH128985.cpp
index 741d1f7ea1d80..1eeb1d17678de 100644
--- a/clang/test/CodeGenCXX/GH128985.cpp
+++ b/clang/test/CodeGenCXX/GH128985.cpp
@@ -207,3 +207,16 @@ void f12() {
 #embed <embed-data.txt> limit(2)
   };
 }
+
+// Complex elements are converted from one data element each.
+// CHECK-LABEL: define {{.*}}void @_Z3f13v(
+// CHECK: call {{.*}}ptr @_Znam(i64 noundef 32)
+// CHECK: store double 4.800000e+01, ptr
+// CHECK: store double 0.000000e+00, ptr
+// CHECK: store double 4.900000e+01, ptr
+// CHECK: store double 0.000000e+00, ptr
+void f13() {
+  _Complex double *p = new _Complex double[]{
+#embed <embed-data.txt> limit(2)
+  };
+}
diff --git a/clang/test/SemaCXX/GH128985.cpp b/clang/test/SemaCXX/GH128985.cpp
index faa6eeab3a253..0d51e3e53582f 100644
--- a/clang/test/SemaCXX/GH128985.cpp
+++ b/clang/test/SemaCXX/GH128985.cpp
@@ -45,6 +45,10 @@ void f(int x) {
   };
 
   A v[] = {1, 2, 3,
+#embed __FILE__ limit(10)
+  };
+
+  _Complex double *w = new _Complex double[]{
 #embed __FILE__ limit(10)
   };
 }

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

Reply via email to