https://github.com/adams381 updated 
https://github.com/llvm/llvm-project/pull/201654

>From dffed1eb7570356d7e053bbe7e6c2c2af2314170 Mon Sep 17 00:00:00 2001
From: Adam Smith <[email protected]>
Date: Thu, 4 Jun 2026 10:41:58 -0700
Subject: [PATCH 1/4] [CIR] Handle non-zero-initializable types in
 emitNullInitialization

Value-initializing an aggregate containing a pointer-to-data-member
(e.g. 'new Inner()' where Inner has an 'int Inner::*' field) crashed
with "type is not zero initializable" because emitNullInitialization
unconditionally called errorNYI for types where isZeroInitializable
returns false.

Member pointers use -1 as the null/invalid sentinel, so a plain zero
store is incorrect.  Replace the errorNYI with a call to
emitNullConstant, which already builds the correct per-field pattern
(-1 for member-pointer fields, zero elsewhere), and store the result.
Types with virtual bases are still guarded with errorNYI since
emitNullConstant does not yet handle them.
---
 clang/lib/CIR/CodeGen/CIRGenFunction.cpp      | 15 ++++--
 .../CIR/CodeGen/member-pointer-null-init.cpp  | 47 +++++++++++++++++++
 2 files changed, 59 insertions(+), 3 deletions(-)
 create mode 100644 clang/test/CIR/CodeGen/member-pointer-null-init.cpp

diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp 
b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
index 4ecb47a864146..f16a5e6eb1574 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -1302,10 +1302,19 @@ void 
CIRGenFunction::emitNullInitialization(mlir::Location loc, Address destPtr,
 
   // If the type contains a pointer to data member we can't memset it to zero.
   // Instead, create a null constant and copy it to the destination.
-  // TODO: there are other patterns besides zero that we can usefully memset,
-  // like -1, which happens to be the pattern used by member-pointers.
+  // Member pointers use -1 as the null value, so a plain zero store would be
+  // incorrect; emitNullConstant produces the right per-field pattern.
   if (!cgm.getTypes().isZeroInitializable(ty)) {
-    cgm.errorNYI(loc, "type is not zero initializable");
+    // emitNullConstant does not yet handle types with virtual bases.
+    if (const auto *rd = ty->getAsCXXRecordDecl(); rd && rd->getNumVBases()) {
+      cgm.errorNYI(loc,
+                   "emitNullInitialization: non-zero-init type with virtual "
+                   "bases");
+      return;
+    }
+    mlir::Value nullVal = cgm.emitNullConstant(ty, loc);
+    builder.createStore(loc, nullVal, destPtr);
+    return;
   }
 
   // In LLVM Codegen: otherwise, just memset the whole thing to zero using
diff --git a/clang/test/CIR/CodeGen/member-pointer-null-init.cpp 
b/clang/test/CIR/CodeGen/member-pointer-null-init.cpp
new file mode 100644
index 0000000000000..74ee344c6f407
--- /dev/null
+++ b/clang/test/CIR/CodeGen/member-pointer-null-init.cpp
@@ -0,0 +1,47 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++17 -fclangir 
-emit-cir %s -o %t.cir
+// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++17 -fclangir 
-emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --check-prefix=LLVM,LLVMCIR --input-file=%t-cir.ll %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++17 -emit-llvm %s 
-o %t.ll
+// RUN: FileCheck --check-prefix=LLVM,OGCG --input-file=%t.ll %s
+
+struct Inner {
+  int Inner::*p;
+};
+
+struct Outer {
+  Inner a;
+  int b;
+};
+
+// Value-init of a heap-allocated struct containing a pointer-to-data-member.
+// The member pointer is null (-1), so the stored constant must carry -1.
+
+// CIR-LABEL: cir.func {{.*}}@_Z8make_newv
+// CIR:         [[NULL:%.*]] = cir.const #cir.const_record<{#cir.int<-1> : 
!s64i}> : !rec_Inner
+// CIR:         cir.store align(8) [[NULL]], {{%.*}} : !rec_Inner, 
!cir.ptr<!rec_Inner>
+
+// LLVMCIR-LABEL: define {{.*}} ptr @_Z8make_newv
+// LLVMCIR:         call {{.*}} @_Znwm
+// LLVMCIR:         store %struct.Inner { i64 -1 }, ptr %{{.*}}, align 8
+
+// OGCG: @{{.*}} = private constant %struct.Inner { i64 -1 }
+// OGCG-LABEL: define {{.*}} ptr @_Z8make_newv
+// OGCG:         call {{.*}} @llvm.memcpy{{.*}}i64 8
+
+Inner *make_new() { return new Inner(); }
+
+// Partial aggregate init: Inner subobject 'a' is value-initialized because
+// it has no designated initializer.
+
+// CIR-LABEL: cir.func {{.*}}@_Z11runtime_aggi
+// CIR:         cir.const #cir.int<-1> : !s64i
+// CIR:         cir.store align(8) {{%.*}}, {{%.*}} : !s64i
+
+// LLVM-LABEL: define {{.*}} void @_Z11runtime_aggi
+// LLVM:          store i64 -1, ptr %{{.*}}, align 8
+
+void runtime_agg(int x) {
+  Outer o = {.b = x};
+  (void)o;
+}

>From 6bb221ecfc707b4ff6cc62f678d4282803b4e7d6 Mon Sep 17 00:00:00 2001
From: Adam Smith <[email protected]>
Date: Tue, 9 Jun 2026 09:41:31 -0700
Subject: [PATCH 2/4] [CIR] Fix null-init for non-zero-init aggregates

Value-initializing an aggregate containing a pointer-to-data-member
(e.g. 'new Inner()' where Inner has an 'int Inner::*' field) crashed
with "type is not zero initializable" because emitNullInitialization
unconditionally called errorNYI for types where isZeroInitializable
returns false.

Member pointers use -1 as the null/invalid sentinel, so a plain zero
store is incorrect.  Replace the errorNYI with a call to
emitNullConstant, which already builds the correct per-field pattern
(-1 for member-pointer fields, zero elsewhere), and store the result.
Types with virtual bases are still guarded with errorNYI since
emitNullConstant does not yet handle them.
---
 clang/lib/CIR/CodeGen/CIRGenFunction.cpp | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp 
b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
index f16a5e6eb1574..0a392b4202625 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -1302,10 +1302,15 @@ void 
CIRGenFunction::emitNullInitialization(mlir::Location loc, Address destPtr,
 
   // If the type contains a pointer to data member we can't memset it to zero.
   // Instead, create a null constant and copy it to the destination.
-  // Member pointers use -1 as the null value, so a plain zero store would be
-  // incorrect; emitNullConstant produces the right per-field pattern.
+  // TODO: there are other patterns besides zero that we can usefully memset,
+  // like -1, which happens to be the pattern used by member-pointers.
   if (!cgm.getTypes().isZeroInitializable(ty)) {
-    // emitNullConstant does not yet handle types with virtual bases.
+    // Classic codegen handles non-zero-init VLAs here via emitNonZeroVLAInit.
+    // In CIR, getTypeSizeInChars returns 0 for VLAs, so they are caught by
+    // the errorNYI above.
+    //
+    // Guard: emitNullConstant calls errorNYI for virtual bases and returns {},
+    // which would crash builder.getConstant; report the NYI here instead.
     if (const auto *rd = ty->getAsCXXRecordDecl(); rd && rd->getNumVBases()) {
       cgm.errorNYI(loc,
                    "emitNullInitialization: non-zero-init type with virtual "

>From 1ce4474755e14efd2223b98f5f2f11d962f220f8 Mon Sep 17 00:00:00 2001
From: Adam Smith <[email protected]>
Date: Mon, 22 Jun 2026 10:31:07 -0700
Subject: [PATCH 3/4] [CIR] Assert vbases in emitNullInitialization

Replace the virtual-base errorNYI guard in emitNullInitialization with
an assert.  A record only reaches the non-zero-initializable branch
because it contains a pointer to data member, and emitNullConstant
already reports its own NYI for the constant shapes it cannot build, so
the separate guard here was redundant.  Virtual bases are the one
reachable shape emitNullConstant cannot lower yet; until there is test
coverage matching the classic output for that case, assert they are
absent rather than emitting an untested constant.
---
 clang/lib/CIR/CodeGen/CIRGenFunction.cpp | 20 ++++++++------------
 1 file changed, 8 insertions(+), 12 deletions(-)

diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp 
b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
index 0a392b4202625..aa5b95c66358c 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -1305,18 +1305,14 @@ void 
CIRGenFunction::emitNullInitialization(mlir::Location loc, Address destPtr,
   // TODO: there are other patterns besides zero that we can usefully memset,
   // like -1, which happens to be the pattern used by member-pointers.
   if (!cgm.getTypes().isZeroInitializable(ty)) {
-    // Classic codegen handles non-zero-init VLAs here via emitNonZeroVLAInit.
-    // In CIR, getTypeSizeInChars returns 0 for VLAs, so they are caught by
-    // the errorNYI above.
-    //
-    // Guard: emitNullConstant calls errorNYI for virtual bases and returns {},
-    // which would crash builder.getConstant; report the NYI here instead.
-    if (const auto *rd = ty->getAsCXXRecordDecl(); rd && rd->getNumVBases()) {
-      cgm.errorNYI(loc,
-                   "emitNullInitialization: non-zero-init type with virtual "
-                   "bases");
-      return;
-    }
+    // The only non-zero-initializable shape exercised here is an aggregate
+    // whose null pattern comes from a pointer to data member, which
+    // emitNullConstant fills in per field (-1 for member-pointer fields).
+    // Virtual bases are the one reachable shape it cannot lower yet; assert
+    // they are absent rather than emitting an untested constant.
+    [[maybe_unused]] const auto *rd = ty->getAsCXXRecordDecl();
+    assert((!rd || rd->getNumVBases() == 0) &&
+           "emitNullInitialization: virtual bases not yet supported");
     mlir::Value nullVal = cgm.emitNullConstant(ty, loc);
     builder.createStore(loc, nullVal, destPtr);
     return;

>From a705e5fe42a9e854dbc7f08e9b2596d8e1d15600 Mon Sep 17 00:00:00 2001
From: Adam Smith <[email protected]>
Date: Tue, 23 Jun 2026 09:49:13 -0700
Subject: [PATCH 4/4] [CIR] Assert null-init handles only tested shape

emitNullInitialization's non-zero-initializable branch previously
asserted that the type had no virtual bases.  That guard was redundant:
emitNullConstant already reports the NYI for virtual bases (and for
non-zero-init arrays), so this call site does not need to.

Replace it with the assert the path actually wants -- that the type is
the pointer-to-data-member case the tests cover.  Inside the
!isZeroInitializable branch an Itanium record is non-zero-initializable
only because it transitively contains a pointer to data member, so
isMemberDataPointerType() || isRecordType() pins exactly that case; any
other shape (e.g. a non-zero-init array) now trips the assert instead of
emitting an untested constant.
---
 clang/lib/CIR/CodeGen/CIRGenFunction.cpp | 14 ++++++--------
 1 file changed, 6 insertions(+), 8 deletions(-)

diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp 
b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
index aa5b95c66358c..dd733216f8268 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -1305,14 +1305,12 @@ void 
CIRGenFunction::emitNullInitialization(mlir::Location loc, Address destPtr,
   // TODO: there are other patterns besides zero that we can usefully memset,
   // like -1, which happens to be the pattern used by member-pointers.
   if (!cgm.getTypes().isZeroInitializable(ty)) {
-    // The only non-zero-initializable shape exercised here is an aggregate
-    // whose null pattern comes from a pointer to data member, which
-    // emitNullConstant fills in per field (-1 for member-pointer fields).
-    // Virtual bases are the one reachable shape it cannot lower yet; assert
-    // they are absent rather than emitting an untested constant.
-    [[maybe_unused]] const auto *rd = ty->getAsCXXRecordDecl();
-    assert((!rd || rd->getNumVBases() == 0) &&
-           "emitNullInitialization: virtual bases not yet supported");
+    // Only the pointer-to-data-member case is tested here; emitNullConstant
+    // owns the NYIs for shapes it cannot build (virtual bases, non-zero-init
+    // arrays).
+    assert((ty->isMemberDataPointerType() || ty->isRecordType()) &&
+           "emitNullInitialization: only pointer-to-data-member (directly or "
+           "within a record) null initialization is implemented");
     mlir::Value nullVal = cgm.emitNullConstant(ty, loc);
     builder.createStore(loc, nullVal, destPtr);
     return;

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

Reply via email to