[clang] a0ffdf2 - [CIR] Allow replacement of a structor declaration with an alias (#188320)

via cfe-commits cfe-commits at lists.llvm.org
Mon Mar 30 13:51:35 PDT 2026


Author: Andy Kaylor
Date: 2026-03-30T13:51:29-07:00
New Revision: a0ffdf28500e21905cab02d1df28c9cef3581155

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

LOG: [CIR] Allow replacement of a structor declaration with an alias (#188320)

We had an errorNYI diagnostic to trigger when we generated an alias for
a ctor or dtor that had an existing declaration. Because functions are
used via flat symbol references, all that is needed is to erase the old
declaration. This change does that.

Added: 
    clang/test/CIR/CodeGen/ctor-alias-prev-decl.cpp
    clang/test/CIR/CodeGen/dtor-alias-prev-decl.cpp

Modified: 
    clang/lib/CIR/CodeGen/CIRGenModule.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index de68927089873..40fc0bfaf7eb6 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -3104,9 +3104,15 @@ void CIRGenModule::emitAliasForGlobal(StringRef mangledName,
   // Alias constructors and destructors are always unnamed_addr.
   assert(!cir::MissingFeatures::opGlobalUnnamedAddr());
 
-  // Switch any previous uses to the alias.
   if (op) {
-    errorNYI(aliasFD->getSourceRange(), "emitAliasForGlobal: previous uses");
+    // Any existing users of the existing function declaration will be
+    // referencing the function by flat symbol reference (i.e. the name), so
+    // those uses will automatically resolve to the alias now that we've
+    // replaced the function declaration. We can safely erase the existing
+    // function declaration.
+    assert(cast<cir::FuncOp>(op).getFunctionType() == alias.getFunctionType() &&
+           "declaration exists with 
diff erent type");
+    op->erase();
   } else {
     // Name already set by createCIRFunction
   }

diff  --git a/clang/test/CIR/CodeGen/ctor-alias-prev-decl.cpp b/clang/test/CIR/CodeGen/ctor-alias-prev-decl.cpp
new file mode 100644
index 0000000000000..31d2801bd06f4
--- /dev/null
+++ b/clang/test/CIR/CodeGen/ctor-alias-prev-decl.cpp
@@ -0,0 +1,43 @@
+// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -mconstructor-aliases -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s
+// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -mconstructor-aliases -emit-llvm %s -o %t.ll
+// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s
+
+// Test that constructor aliases work correctly when the call site is emitted
+// before the constructor definition. This creates a declaration for the
+// complete constructor first, which is then replaced by the alias.
+
+struct B {
+  B();
+};
+
+void bar() {
+  B b;
+}
+
+B::B() {
+}
+
+// CHECK: cir.func{{.*}} @_Z3barv()
+// CHECK:   %[[B:.*]] = cir.alloca !rec_B, !cir.ptr<!rec_B>, ["b", init]
+// CHECK:   cir.call @_ZN1BC1Ev(%[[B]]) : (!cir.ptr<!rec_B> {{.*}}) -> ()
+// CHECK:   cir.return
+
+// CHECK: cir.func{{.*}} @_ZN1BC2Ev(%arg0: !cir.ptr<!rec_B>
+// CHECK:   %[[THIS_ADDR:.*]] = cir.alloca !cir.ptr<!rec_B>, !cir.ptr<!cir.ptr<!rec_B>>, ["this", init]
+// CHECK:   cir.store %arg0, %[[THIS_ADDR]]
+// CHECK:   %[[THIS:.*]] = cir.load %[[THIS_ADDR]] : !cir.ptr<!cir.ptr<!rec_B>>, !cir.ptr<!rec_B>
+
+// CHECK: cir.func{{.*}} private dso_local @_ZN1BC1Ev(!cir.ptr<!rec_B>) alias(@_ZN1BC2Ev)
+
+// OGCG: @_ZN1BC1Ev = unnamed_addr alias void (ptr), ptr @_ZN1BC2Ev
+
+// OGCG: define{{.*}} void @_Z3barv()
+// OGCG:   %[[B:.*]] = alloca %struct.B, align 1
+// OGCG:   call void @_ZN1BC1Ev(ptr{{.*}} %[[B]])
+// OGCG:   ret void
+
+// OGCG: define{{.*}} @_ZN1BC2Ev(ptr{{.*}} %[[THIS_ARG:.*]])
+// OGCG:   %[[THIS_ADDR:.*]] = alloca ptr
+// OGCG:   store ptr %[[THIS_ARG]], ptr %[[THIS_ADDR]]
+// OGCG:   %[[THIS:.*]] = load ptr, ptr %[[THIS_ADDR]]

diff  --git a/clang/test/CIR/CodeGen/dtor-alias-prev-decl.cpp b/clang/test/CIR/CodeGen/dtor-alias-prev-decl.cpp
new file mode 100644
index 0000000000000..cb920e875d78e
--- /dev/null
+++ b/clang/test/CIR/CodeGen/dtor-alias-prev-decl.cpp
@@ -0,0 +1,42 @@
+// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -mconstructor-aliases -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s
+// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -mconstructor-aliases -emit-llvm %s -o %t.ll
+// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s
+
+// Test that destructor aliases work correctly when the call site is emitted
+// before the destructor definition. This creates a declaration for the
+// complete destructor first, which is then replaced by the alias.
+
+struct B {
+  ~B();
+};
+
+void baz() {
+  B b;
+}
+
+B::~B() {
+}
+
+// CHECK: cir.func{{.*}} @_Z3bazv()
+// CHECK:   %[[B:.*]] = cir.alloca !rec_B, !cir.ptr<!rec_B>, ["b"]
+// CHECK:   cir.call @_ZN1BD1Ev(%[[B]]) nothrow : (!cir.ptr<!rec_B> {{.*}}) -> ()
+
+// CHECK: cir.func{{.*}} @_ZN1BD2Ev(%arg0: !cir.ptr<!rec_B>
+// CHECK:   %[[THIS_ADDR:.*]] = cir.alloca !cir.ptr<!rec_B>, !cir.ptr<!cir.ptr<!rec_B>>, ["this", init]
+// CHECK:   cir.store %arg0, %[[THIS_ADDR]]
+// CHECK:   %[[THIS:.*]] = cir.load %[[THIS_ADDR]] : !cir.ptr<!cir.ptr<!rec_B>>, !cir.ptr<!rec_B>
+
+// CHECK: cir.func{{.*}} private dso_local @_ZN1BD1Ev(!cir.ptr<!rec_B>) alias(@_ZN1BD2Ev)
+
+// OGCG: @_ZN1BD1Ev = unnamed_addr alias void (ptr), ptr @_ZN1BD2Ev
+
+// OGCG: define{{.*}} void @_Z3bazv()
+// OGCG:   %[[B:.*]] = alloca %struct.B, align 1
+// OGCG:   call void @_ZN1BD1Ev(ptr{{.*}} %[[B]])
+// OGCG:   ret void
+
+// OGCG: define{{.*}} @_ZN1BD2Ev(ptr{{.*}} %[[THIS_ARG:.*]])
+// OGCG:   %[[THIS_ADDR:.*]] = alloca ptr
+// OGCG:   store ptr %[[THIS_ARG]], ptr %[[THIS_ADDR]]
+// OGCG:   %[[THIS:.*]] = load ptr, ptr %[[THIS_ADDR]]


        


More information about the cfe-commits mailing list