[clang] [clang][CodeGen] Evaluate `typeid` operands which are the most derived objects (PR #208470)
Yanzuo Liu via cfe-commits
cfe-commits at lists.llvm.org
Thu Jul 9 07:17:46 PDT 2026
https://github.com/zwuis created https://github.com/llvm/llvm-project/pull/208470
Before this patch, `typeid` operands are not evaluated if they are the most derived objects.
Before #196544, only `typeid(non-reference-var)` is considered to be the most derived object, which doesn't have side effects obviously. So there was no miscompilation.
Fixes #202051.
>From 2603e1a3f88dfabf5d2e2794274b308a2bcceebc Mon Sep 17 00:00:00 2001
From: Yanzuo Liu <zwuis at outlook.com>
Date: Thu, 9 Jul 2026 22:15:15 +0800
Subject: [PATCH] Evaluate `typeid` operands which are the most derived objects
---
clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp | 19 +--
clang/lib/CodeGen/CGExprCXX.cpp | 22 +--
.../CIR/CodeGenCXX/typeid-most-derived.cpp | 134 ++++++++++++++++++
clang/test/CodeGenCXX/typeid-most-derived.cpp | 13 ++
4 files changed, 169 insertions(+), 19 deletions(-)
create mode 100644 clang/test/CIR/CodeGenCXX/typeid-most-derived.cpp
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
index df60005d77259..0f0c5e6be13e9 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
@@ -1916,9 +1916,8 @@ mlir::Value CIRGenFunction::emitDynamicCast(Address thisAddr,
destCirTy, isRefCast, thisAddr);
}
-static mlir::Value emitCXXTypeidFromVTable(CIRGenFunction &cgf, const Expr *e,
- mlir::Type typeInfoPtrTy,
- bool hasNullCheck) {
+static Address emitCXXTypeidOperand(CIRGenFunction &cgf, const Expr *e,
+ bool hasNullCheck) {
Address thisPtr = cgf.emitLValue(e).getAddress();
QualType srcType = e->getType();
@@ -1940,7 +1939,7 @@ static mlir::Value emitCXXTypeidFromVTable(CIRGenFunction &cgf, const Expr *e,
});
}
- return cgf.cgm.getCXXABI().emitTypeid(cgf, srcType, thisPtr, typeInfoPtrTy);
+ return thisPtr;
}
mlir::Value CIRGenFunction::emitCXXTypeidExpr(const CXXTypeidExpr *e) {
@@ -1958,11 +1957,13 @@ mlir::Value CIRGenFunction::emitCXXTypeidExpr(const CXXTypeidExpr *e) {
// polymorphic class type, the result refers to a std::type_info object
// representing the type of the most derived object (that is, the dynamic
// type) to which the glvalue refers.
- // If the operand is already most derived object, no need to look up vtable.
- if (!e->isTypeOperand() && e->isPotentiallyEvaluated() &&
- !e->isMostDerived(getContext()))
- return emitCXXTypeidFromVTable(*this, e->getExprOperand(), resultType,
- e->hasNullCheck());
+ if (!e->isTypeOperand() && e->isPotentiallyEvaluated()) {
+ Address thisPtr =
+ emitCXXTypeidOperand(*this, e->getExprOperand(), e->hasNullCheck());
+ if (!e->isMostDerived(getContext()))
+ return cgm.getCXXABI().emitTypeid(*this, ty, thisPtr, resultType);
+ // If the operand is already most derived object, no need to look up vtable.
+ }
auto typeInfo =
cast<cir::GlobalViewAttr>(cgm.getAddrOfRTTIDescriptor(loc, ty));
diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp
index ebbc0addfed2c..ec4ae54354623 100644
--- a/clang/lib/CodeGen/CGExprCXX.cpp
+++ b/clang/lib/CodeGen/CGExprCXX.cpp
@@ -2173,9 +2173,8 @@ void CodeGenFunction::EmitCXXDeleteExpr(const CXXDeleteExpr *E) {
}
}
-static llvm::Value *EmitTypeidFromVTable(CodeGenFunction &CGF, const Expr *E,
- llvm::Type *StdTypeInfoPtrTy,
- bool HasNullCheck) {
+static Address EmitTypeidOperand(CodeGenFunction &CGF, const Expr *E,
+ bool HasNullCheck) {
// Get the vtable pointer.
Address ThisPtr = CGF.EmitLValue(E).getAddress();
@@ -2205,8 +2204,7 @@ static llvm::Value *EmitTypeidFromVTable(CodeGenFunction &CGF, const Expr *E,
CGF.EmitBlock(EndBlock);
}
- return CGF.CGM.getCXXABI().EmitTypeid(CGF, SrcRecordTy, ThisPtr,
- StdTypeInfoPtrTy);
+ return ThisPtr;
}
llvm::Value *CodeGenFunction::EmitCXXTypeidExpr(const CXXTypeidExpr *E) {
@@ -2230,17 +2228,21 @@ llvm::Value *CodeGenFunction::EmitCXXTypeidExpr(const CXXTypeidExpr *E) {
return MaybeASCast(TypeInfo);
}
+ const Expr *Operand = E->getExprOperand();
+ QualType OperandTy = Operand->getType();
+
// C++ [expr.typeid]p2:
// When typeid is applied to a glvalue expression whose type is a
// polymorphic class type, the result refers to a std::type_info object
// representing the type of the most derived object (that is, the dynamic
// type) to which the glvalue refers.
- // If the operand is already most derived object, no need to look up vtable.
- if (E->isPotentiallyEvaluated() && !E->isMostDerived(getContext()))
- return EmitTypeidFromVTable(*this, E->getExprOperand(), PtrTy,
- E->hasNullCheck());
+ if (E->isPotentiallyEvaluated()) {
+ Address ThisPtr = EmitTypeidOperand(*this, Operand, E->hasNullCheck());
+ if (!E->isMostDerived(getContext()))
+ return CGM.getCXXABI().EmitTypeid(*this, OperandTy, ThisPtr, PtrTy);
+ // If the operand is already most derived object, no need to look up vtable.
+ }
- QualType OperandTy = E->getExprOperand()->getType();
return MaybeASCast(CGM.GetAddrOfRTTIDescriptor(OperandTy));
}
diff --git a/clang/test/CIR/CodeGenCXX/typeid-most-derived.cpp b/clang/test/CIR/CodeGenCXX/typeid-most-derived.cpp
new file mode 100644
index 0000000000000..369a0e4e3ab7a
--- /dev/null
+++ b/clang/test/CIR/CodeGenCXX/typeid-most-derived.cpp
@@ -0,0 +1,134 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir -mmlir --mlir-print-ir-before=cir-lowering-prepare %s -o %t.cir 2> %t-before.cir
+// RUN: FileCheck %s --input-file=%t-before.cir --check-prefixes=CIR
+// RUN: FileCheck %s --input-file=%t.cir --check-prefixes=CIR
+// RUN: %clang_cc1 %s -triple %itanium_abi_triple -Wno-unused-value -std=c++11 -fclangir -emit-llvm -o - -std=c++11 | FileCheck %s --check-prefixes=CIR-TO-LLVM
+// RUN: %clang_cc1 %s -triple %itanium_abi_triple -Wno-unused-value -std=c++11 -emit-llvm -o - -std=c++11 | FileCheck %s --check-prefixes=LLVM
+
+// FIXME: CIR-TO-LLVM output is incorrect. It should be same as LLVM output.
+
+namespace std {
+ class type_info {};
+}
+
+struct Base {
+ virtual int foo() { return 42; }
+ virtual ~Base();
+};
+
+struct NonFinal : Base {};
+struct Final final : Base {
+ int foo() override { return 84; }
+};
+
+void func();
+
+// Most derived
+void base_by_value(Base b) { typeid(b); }
+// CIR-LABEL: cir.func {{.*}}@_Z13base_by_value4Base
+// CIR-NOT: cir.vtable.get_vptr
+// CIR: cir.return
+// LLVM-LABEL: define {{.*}}void @_Z13base_by_value4Base
+// LLVM-NOT: getelementptr
+// LLVM: ret void
+// CIR-TO-LLVM-LABEL: define {{.*}}void @_Z13base_by_value4Base
+// CIR-TO-LLVM-NOT: getelementptr
+// CIR-TO-LLVM: ret void
+
+// Most derived
+void final_ref(Final &f) { typeid(f); }
+// CIR-LABEL: cir.func {{.*}}@_Z9final_refR5Final
+// CIR-NOT: cir.vtable.get_vptr
+// CIR: cir.return
+// LLVM-LABEL: define {{.*}}void @_Z9final_refR5Final
+// LLVM-NOT: getelementptr
+// LLVM: ret void
+// CIR-TO-LLVM-LABEL: define {{.*}}void @_Z9final_refR5Final
+// CIR-TO-LLVM-NOT: getelementptr
+// CIR-TO-LLVM: ret void
+
+// Most derived
+void final_deref(Final *f) { typeid(*f); }
+// CIR-LABEL: cir.func {{.*}}@_Z11final_derefP5Final
+// CIR-NOT: cir.vtable.get_vptr
+// CIR: cir.cmp
+// CIR-NEXT: cir.if
+// CIR-NOT: cir.vtable.get_vptr
+// CIR: cir.return
+// LLVM-LABEL: define {{.*}}void @_Z11final_derefP5Final
+// LLVM-NOT: getelementptr
+// LLVM: icmp eq {{.*}}, null
+// LLVM-NEXT: br i1
+// LLVM-NOT: getelementptr
+// LLVM: ret void
+// CIR-TO-LLVM-LABEL: define {{.*}}void @_Z11final_derefP5Final
+// CIR-TO-LLVM-NOT: getelementptr
+// CIR-TO-LLVM: icmp eq {{.*}}, null
+// CIR-TO-LLVM-NEXT: br i1
+// CIR-TO-LLVM-NOT: getelementptr
+// CIR-TO-LLVM: ret void
+
+// Most derived
+void should_evaluate(Final &f) { typeid(func(), f); }
+// CIR-LABEL: cir.func {{.*}}@_Z15should_evaluateR5Final
+// CIR-NOT: cir.vtable.get_vptr
+// CIR: cir.call @_Z4funcv
+// CIR-NOT: cir.vtable.get_vptr
+// CIR: cir.return
+// LLVM-LABEL: define {{.*}}void @_Z15should_evaluateR5Final
+// LLVM-NOT: getelementptr
+// LLVM: call void @_Z4funcv()
+// LLVM-NOT: getelementptr
+// LLVM: ret void
+// CIR-TO-LLVM-LABEL: define {{.*}}void @_Z15should_evaluateR5Final
+// CIR-TO-LLVM-NOT: getelementptr
+// CIR-TO-LLVM: call void @_Z4funcv()
+// CIR-TO-LLVM-NOT: getelementptr
+// CIR-TO-LLVM: ret void
+
+// Not most derived
+void base_ref(Base &b) { typeid(b); }
+// CIR-LABEL: cir.func {{.*}}@_Z8base_refR4Base
+// CIR : cir.vtable.get_vptr
+// CIR: cir.return
+// LLVM-LABEL: define {{.*}}void @_Z8base_refR4Base
+// LLVM: getelementptr inbounds ptr, ptr
+// LLVM: ret void
+// CIR-TO-LLVM-LABEL: define {{.*}}void @_Z8base_refR4Base
+// CIR-TO-LLVM: getelementptr ptr, ptr
+// CIR-TO-LLVM: ret void
+
+// Not most derived
+void base_deref(Base *b) { typeid(*b); }
+// CIR-LABEL: cir.func {{.*}}@_Z10base_derefP4Base
+// CIR : cir.vtable.get_vptr
+// CIR: cir.return
+// LLVM-LABEL: define {{.*}}void @_Z10base_derefP4Base
+// LLVM: getelementptr inbounds ptr, ptr
+// LLVM: ret void
+// CIR-TO-LLVM-LABEL: define {{.*}}void @_Z10base_derefP4Base
+// CIR-TO-LLVM-NOT: getelementptr
+// CIR-TO-LLVM: ret void
+
+// Not most derived
+void nonfinal_ref(NonFinal &d) { typeid(d); }
+// CIR-LABEL: cir.func {{.*}}@_Z12nonfinal_refR8NonFinal
+// CIR : cir.vtable.get_vptr
+// CIR: cir.return
+// LLVM-LABEL: define {{.*}}void @_Z12nonfinal_refR8NonFinal
+// LLVM: getelementptr inbounds ptr, ptr
+// LLVM: ret void
+// CIR-TO-LLVM-LABEL: define {{.*}}void @_Z12nonfinal_refR8NonFinal
+// CIR-TO-LLVM: getelementptr ptr, ptr
+// CIR-TO-LLVM: ret void
+
+// Not most derived
+void nonfinal_deref(NonFinal *d) { typeid(*d); }
+// CIR-LABEL: cir.func {{.*}}@_Z14nonfinal_derefP8NonFinal
+// CIR : cir.vtable.get_vptr
+// CIR: cir.return
+// LLVM-LABEL: define {{.*}}void @_Z14nonfinal_derefP8NonFinal
+// LLVM: getelementptr inbounds ptr, ptr
+// LLVM: ret void
+// CIR-TO-LLVM-LABEL: define {{.*}}void @_Z14nonfinal_derefP8NonFinal
+// CIR-TO-LLVM-NOT: getelementptr
+// CIR-TO-LLVM: ret void
diff --git a/clang/test/CodeGenCXX/typeid-most-derived.cpp b/clang/test/CodeGenCXX/typeid-most-derived.cpp
index 2b6bb850ff415..99c743acfcaaf 100644
--- a/clang/test/CodeGenCXX/typeid-most-derived.cpp
+++ b/clang/test/CodeGenCXX/typeid-most-derived.cpp
@@ -14,6 +14,8 @@ struct Final final : Base {
int foo() override { return 84; }
};
+void func();
+
// Most derived
void base_by_value(Base b) { typeid(b); }
// CHECK-LABEL: define {{.*}}void @_Z13base_by_value4Base
@@ -30,6 +32,17 @@ void final_ref(Final &f) { typeid(f); }
void final_deref(Final *f) { typeid(*f); }
// CHECK-LABEL: define {{.*}}void @_Z11final_derefP5Final
// CHECK-NOT: %vtable
+// CHECK: icmp eq {{.*}}, null
+// CHECK-NEXT: br i1
+// CHECK-NOT: %vtable
+// CHECK: ret void
+
+// Most derived
+void should_evaluate(Final &f) { typeid(func(), f); }
+// CHECK-LABEL: define {{.*}}void @_Z15should_evaluateR5Final
+// CHECK-NOT: %vtable
+// CHECK: call void @_Z4funcv()
+// CHECK-NOT: %vtable
// CHECK: ret void
// Not most derived
More information about the cfe-commits
mailing list