[clang] [CIR] Implement vTableClassName for Type::ObjCObject (PR #195232)

Dylan Leifer-Ives via cfe-commits cfe-commits at lists.llvm.org
Fri May 1 00:57:01 PDT 2026


https://github.com/dleiferives created https://github.com/llvm/llvm-project/pull/195232

Implements `Type::ObjCObject` case in `vTableClassNameForType` and `buildTypeInfo` in `CIRGenItaniumCXXABI.cpp`. Mirroring implementation in `ItaniumCXXABI.cpp`.

In `vTableClassNameForType` handles the builtin types directly and returns `__class_type_info`. The ObjCInterface sub-case falls through to `errorNYI` as it belongs to the separate `Type::ObjCInterface` subissue.

Also fixes `builtTypeInfo` for `Type::ObjCObjectPointer`, which was `errorNYI` in #184011 probably over looked as it was handled in `vTableClassNameForType`. This fix to `builtTypeInfo` is load-bearing: `typeid(id)` calls `buildTypeInfo(ObjCObjectPointerType)` first, which must delegate to `buildPointerTypeInfo` before recursing into the `ObjCObject` case. Without it the `rtti-objc.mm` test fails.

`rtti-objc.mm` verifies RTTI emission for `typeid(id)` and `typeid(Class)`. Type operands are used to emit `Type::ObjCObject` for testability as the regular path of `convertType(ObjCObjectPointerType)` is NYI in CIRGenTypes. The test covers both the `ObjCObjectPointer` and `ObjCObject` paths through both CIR and LLVM lowering.

Furthers #163601 

>From a8a6b9895844c730cdeb2ed8aeb232a90f8321a1 Mon Sep 17 00:00:00 2001
From: Dylan Leifer-Ives <dleiferives at gmail.com>
Date: Thu, 30 Apr 2026 13:44:49 -0700
Subject: [PATCH 1/3] [CIR] Implement vTableClassNameForType and buildTypeInfo
 for ObjCObject

Working on issue #163601
Duplicated behaviour found in `ItaniumCXXABI.cpp` per falling through to
`Type::ObjCInterface`.

Per #163850's folding into #184011 it seems that
`CIRGenItaniumRTTIBuilder::buildTypeInfo`'s building of
`Type::ObjCObjectPointer` was overlooked somewhere in the chain. As far
as I can tell `Type::ObjCObject` requires `Type::ObjCObjectPointer` to
build, before it can be emitted anywhere: ie for testing.
---
 clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp | 26 ++++++++++++++++---
 1 file changed, 22 insertions(+), 4 deletions(-)

diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
index 946739d4e1702..2f572e4ec6862 100644
--- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
@@ -1074,8 +1074,15 @@ const char *vTableClassNameForType(const CIRGenModule &cgm, const Type *ty) {
   }
 
   case Type::ObjCObject:
-    cgm.errorNYI("VTableClassNameForType: ObjCObject");
-    break;
+    // Ignore protocol qualifiers.
+    ty = cast<ObjCObjectType>(ty)->getBaseType().getTypePtr();
+
+    // Handle id and Class.
+    if (isa<BuiltinType>(ty))
+      return classTypeInfo;
+
+    assert(isa<ObjCInterfaceType>(ty));
+    [[fallthrough]];
 
   case Type::ObjCInterface:
     cgm.errorNYI("VTableClassNameForType: ObjCInterface");
@@ -1565,16 +1572,27 @@ mlir::Attribute CIRGenItaniumRTTIBuilder::buildTypeInfo(
   }
 
   case Type::ObjCObject:
+    // Drop protocol qualifiers.
+    const Type *T = cast<ObjCObjectType>(ty)->getBaseType().getTypePtr();
+
+    // The builtin types are abi::__class_type_infos and don't require
+    // extra fields.
+    if (isa<BuiltinType>(T))
+      break;
+    [[fallthrough]];
+
   case Type::ObjCInterface:
-    cgm.errorNYI("buildTypeInfo: ObjCObject & ObjCInterface");
+    cgm.errorNYI("buildTypeInfo: ObjCInterface");
     break;
 
   case Type::ObjCObjectPointer:
-    cgm.errorNYI("buildTypeInfo: ObjCObjectPointer");
+    buildPointerTypeInfo(loc, cast<ObjCObjectPointerType>(ty)->getPointeeType());
     break;
 
   case Type::Pointer:
     // We need to get the type info for the pointee type.
+    // TODO@(dleiferives): ask about this before making PR to #163601,
+    // needed for testing
     buildPointerTypeInfo(loc, cast<PointerType>(ty)->getPointeeType());
     break;
 

>From 678e60581d3f58eb6928fbcfac457b257b7866ba Mon Sep 17 00:00:00 2001
From: Dylan Leifer-Ives <dleiferives at gmail.com>
Date: Thu, 30 Apr 2026 20:11:20 -0700
Subject: [PATCH 2/3] [CIR] Fix jump-over-init error in buildTypeInfo
 ObjCObject case

---
 clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp | 8 ++------
 1 file changed, 2 insertions(+), 6 deletions(-)

diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
index 2f572e4ec6862..517fb30642e31 100644
--- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
@@ -1571,15 +1571,14 @@ mlir::Attribute CIRGenItaniumRTTIBuilder::buildTypeInfo(
     break;
   }
 
-  case Type::ObjCObject:
+  case Type::ObjCObject: {
     // Drop protocol qualifiers.
     const Type *T = cast<ObjCObjectType>(ty)->getBaseType().getTypePtr();
-
     // The builtin types are abi::__class_type_infos and don't require
     // extra fields.
     if (isa<BuiltinType>(T))
       break;
-    [[fallthrough]];
+  } [[fallthrough]];
 
   case Type::ObjCInterface:
     cgm.errorNYI("buildTypeInfo: ObjCInterface");
@@ -1590,9 +1589,6 @@ mlir::Attribute CIRGenItaniumRTTIBuilder::buildTypeInfo(
     break;
 
   case Type::Pointer:
-    // We need to get the type info for the pointee type.
-    // TODO@(dleiferives): ask about this before making PR to #163601,
-    // needed for testing
     buildPointerTypeInfo(loc, cast<PointerType>(ty)->getPointeeType());
     break;
 

>From 374ec99f79bee17961cc6eeecd08ee2e7196c46a Mon Sep 17 00:00:00 2001
From: Dylan Leifer-Ives <dleiferives at gmail.com>
Date: Thu, 30 Apr 2026 22:12:07 -0700
Subject: [PATCH 3/3] [CIR] Implement RTTI test for Type::ObjCObject

---
 clang/test/CIR/CodeGen/rtti-objc.mm | 47 +++++++++++++++++++++++++++++
 1 file changed, 47 insertions(+)
 create mode 100644 clang/test/CIR/CodeGen/rtti-objc.mm

diff --git a/clang/test/CIR/CodeGen/rtti-objc.mm b/clang/test/CIR/CodeGen/rtti-objc.mm
new file mode 100644
index 0000000000000..8fceefb0767b8
--- /dev/null
+++ b/clang/test/CIR/CodeGen/rtti-objc.mm
@@ -0,0 +1,47 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir -x objective-c++
+// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll -x objective-c++
+// RUN: FileCheck --input-file=%t-cir.ll %s -check-prefix=LLVM
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll -x objective-c++
+// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG
+
+// RTTI emission of ObjCObject for Objective C builtin types via typeid. Type operands were
+// used to avoid using convertType(ObjCObjectPointerType) as it is NYI in
+// CIRGenTypes.
+
+namespace std { class type_info; }
+
+void f() {
+  const std::type_info &t1 = typeid(id);
+  const std::type_info &t2 = typeid(Class);
+}
+
+// CIR-DAG: cir.global {{.*}} @_ZTS11objc_object = #cir.const_array<"11objc_object" : !cir.array<!s8i x 13>, trailing_zeros> : !cir.array<!s8i x 14>
+// CIR-DAG: cir.global {{.*}} @_ZTI11objc_object = #cir.typeinfo<{#cir.global_view<@_ZTVN10__cxxabiv117__class_type_infoE, [2 : i32]> : !cir.ptr<!u8i>, #cir.global_view<@_ZTS11objc_object> : !cir.ptr<!u8i>}>
+// CIR-DAG: cir.global {{.*}} @_ZTSP11objc_object = #cir.const_array<"P11objc_object" : !cir.array<!s8i x 14>, trailing_zeros> : !cir.array<!s8i x 15>
+// CIR-DAG: cir.global {{.*}} @_ZTIP11objc_object = #cir.typeinfo<{#cir.global_view<@_ZTVN10__cxxabiv119__pointer_type_infoE, [2 : i32]> : !cir.ptr<!u8i>, #cir.global_view<@_ZTSP11objc_object> : !cir.ptr<!u8i>, #cir.int<0> : !u32i, #cir.global_view<@_ZTI11objc_object> : !cir.ptr<!u8i>}>
+
+// CIR-DAG: cir.global {{.*}} @_ZTS10objc_class = #cir.const_array<"10objc_class" : !cir.array<!s8i x 12>, trailing_zeros> : !cir.array<!s8i x 13>
+// CIR-DAG: cir.global {{.*}} @_ZTI10objc_class = #cir.typeinfo<{#cir.global_view<@_ZTVN10__cxxabiv117__class_type_infoE, [2 : i32]> : !cir.ptr<!u8i>, #cir.global_view<@_ZTS10objc_class> : !cir.ptr<!u8i>}>
+// CIR-DAG: cir.global {{.*}} @_ZTSP10objc_class = #cir.const_array<"P10objc_class" : !cir.array<!s8i x 13>, trailing_zeros> : !cir.array<!s8i x 14>
+// CIR-DAG: cir.global {{.*}} @_ZTIP10objc_class = #cir.typeinfo<{#cir.global_view<@_ZTVN10__cxxabiv119__pointer_type_infoE, [2 : i32]> : !cir.ptr<!u8i>, #cir.global_view<@_ZTSP10objc_class> : !cir.ptr<!u8i>, #cir.int<0> : !u32i, #cir.global_view<@_ZTI10objc_class> : !cir.ptr<!u8i>}>
+
+// LLVM-DAG: @_ZTS11objc_object = linkonce_odr global [14 x i8] c"11objc_object\00", comdat
+// LLVM-DAG: @_ZTI11objc_object = linkonce_odr constant { ptr, ptr } { ptr getelementptr (i8, ptr @_ZTVN10__cxxabiv117__class_type_infoE, i64 16), ptr @_ZTS11objc_object }, comdat
+// LLVM-DAG: @_ZTSP11objc_object = linkonce_odr global [15 x i8] c"P11objc_object\00", comdat
+// LLVM-DAG: @_ZTIP11objc_object = linkonce_odr constant { ptr, ptr, i32, ptr } { ptr getelementptr (i8, ptr @_ZTVN10__cxxabiv119__pointer_type_infoE, i64 16), ptr @_ZTSP11objc_object, i32 0, ptr @_ZTI11objc_object }, comdat
+
+// LLVM-DAG: @_ZTS10objc_class = linkonce_odr global [13 x i8] c"10objc_class\00", comdat
+// LLVM-DAG: @_ZTI10objc_class = linkonce_odr constant { ptr, ptr } { ptr getelementptr (i8, ptr @_ZTVN10__cxxabiv117__class_type_infoE, i64 16), ptr @_ZTS10objc_class }, comdat
+// LLVM-DAG: @_ZTSP10objc_class = linkonce_odr global [14 x i8] c"P10objc_class\00", comdat
+// LLVM-DAG: @_ZTIP10objc_class = linkonce_odr constant { ptr, ptr, i32, ptr } { ptr getelementptr (i8, ptr @_ZTVN10__cxxabiv119__pointer_type_infoE, i64 16), ptr @_ZTSP10objc_class, i32 0, ptr @_ZTI10objc_class }, comdat
+
+// OGCG-DAG: @_ZTS11objc_object = linkonce_odr constant [14 x i8] c"11objc_object\00", comdat
+// OGCG-DAG: @_ZTI11objc_object = linkonce_odr constant { ptr, ptr } { ptr getelementptr inbounds (ptr, ptr @_ZTVN10__cxxabiv117__class_type_infoE, i64 2), ptr @_ZTS11objc_object }, comdat
+// OGCG-DAG: @_ZTSP11objc_object = linkonce_odr constant [15 x i8] c"P11objc_object\00", comdat
+// OGCG-DAG: @_ZTIP11objc_object = linkonce_odr constant { ptr, ptr, i32, ptr } { ptr getelementptr inbounds (ptr, ptr @_ZTVN10__cxxabiv119__pointer_type_infoE, i64 2), ptr @_ZTSP11objc_object, i32 0, ptr @_ZTI11objc_object }, comdat
+
+// OGCG-DAG: @_ZTS10objc_class = linkonce_odr constant [13 x i8] c"10objc_class\00", comdat
+// OGCG-DAG: @_ZTI10objc_class = linkonce_odr constant { ptr, ptr } { ptr getelementptr inbounds (ptr, ptr @_ZTVN10__cxxabiv117__class_type_infoE, i64 2), ptr @_ZTS10objc_class }, comdat
+// OGCG-DAG: @_ZTSP10objc_class = linkonce_odr constant [14 x i8] c"P10objc_class\00", comdat
+// OGCG-DAG: @_ZTIP10objc_class = linkonce_odr constant { ptr, ptr, i32, ptr } { ptr getelementptr inbounds (ptr, ptr @_ZTVN10__cxxabiv119__pointer_type_infoE, i64 2), ptr @_ZTSP10objc_class, i32 0, ptr @_ZTI10objc_class }, comdat



More information about the cfe-commits mailing list