[clang] ede9dd3 - [CIR] Handle dynamic cast to null (#164732)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Oct 23 10:01:12 PDT 2025
Author: Andy Kaylor
Date: 2025-10-23T10:01:08-07:00
New Revision: ede9dd3951e29632a29e13b2fd8c4b6b9d185643
URL: https://github.com/llvm/llvm-project/commit/ede9dd3951e29632a29e13b2fd8c4b6b9d185643
DIFF: https://github.com/llvm/llvm-project/commit/ede9dd3951e29632a29e13b2fd8c4b6b9d185643.diff
LOG: [CIR] Handle dynamic cast to null (#164732)
This adds support for handling dynamic casts that are known at compile
time to always result in a null pointer. For pointer casts, this emits a
null pointer value. For reference casts, it calls the __bad_cast
function.
Added:
Modified:
clang/lib/CIR/CodeGen/CIRGenCXXABI.h
clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
clang/test/CIR/CodeGen/dynamic-cast-exact.cpp
Removed:
################################################################################
diff --git a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h
index c78f9b0ea8a26..d3c7dac04dc67 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h
+++ b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h
@@ -124,6 +124,8 @@ class CIRGenCXXABI {
virtual void emitRethrow(CIRGenFunction &cgf, bool isNoReturn) = 0;
virtual void emitThrow(CIRGenFunction &cgf, const CXXThrowExpr *e) = 0;
+ virtual void emitBadCastCall(CIRGenFunction &cgf, mlir::Location loc) = 0;
+
virtual mlir::Attribute getAddrOfRTTIDescriptor(mlir::Location loc,
QualType ty) = 0;
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
index fe9e21064263b..a3cdf192bf9b8 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
@@ -801,6 +801,26 @@ void CIRGenFunction::emitDeleteCall(const FunctionDecl *deleteFD,
emitNewDeleteCall(*this, deleteFD, deleteFTy, deleteArgs);
}
+static mlir::Value emitDynamicCastToNull(CIRGenFunction &cgf,
+ mlir::Location loc, QualType destTy) {
+ mlir::Type destCIRTy = cgf.convertType(destTy);
+ assert(mlir::isa<cir::PointerType>(destCIRTy) &&
+ "result of dynamic_cast should be a ptr");
+
+ if (!destTy->isPointerType()) {
+ mlir::Region *currentRegion = cgf.getBuilder().getBlock()->getParent();
+ /// C++ [expr.dynamic.cast]p9:
+ /// A failed cast to reference type throws std::bad_cast
+ cgf.cgm.getCXXABI().emitBadCastCall(cgf, loc);
+
+ // The call to bad_cast will terminate the current block. Create a new block
+ // to hold any follow up code.
+ cgf.getBuilder().createBlock(currentRegion, currentRegion->end());
+ }
+
+ return cgf.getBuilder().getNullPtr(destCIRTy, loc);
+}
+
mlir::Value CIRGenFunction::emitDynamicCast(Address thisAddr,
const CXXDynamicCastExpr *dce) {
mlir::Location loc = getLoc(dce->getSourceRange());
@@ -831,10 +851,8 @@ mlir::Value CIRGenFunction::emitDynamicCast(Address thisAddr,
assert(srcRecordTy->isRecordType() && "source type must be a record type!");
assert(!cir::MissingFeatures::emitTypeCheck());
- if (dce->isAlwaysNull()) {
- cgm.errorNYI(dce->getSourceRange(), "emitDynamicCastToNull");
- return {};
- }
+ if (dce->isAlwaysNull())
+ return emitDynamicCastToNull(*this, loc, destTy);
auto destCirTy = mlir::cast<cir::PointerType>(convertType(destTy));
return cgm.getCXXABI().emitDynamicCast(*this, loc, srcRecordTy, destRecordTy,
diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
index f7c4d181cba66..2dce0b16e3043 100644
--- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
@@ -120,6 +120,8 @@ class CIRGenItaniumCXXABI : public CIRGenCXXABI {
return true;
}
+ void emitBadCastCall(CIRGenFunction &cgf, mlir::Location loc) override;
+
mlir::Value
getVirtualBaseClassOffset(mlir::Location loc, CIRGenFunction &cgf,
Address thisAddr, const CXXRecordDecl *classDecl,
@@ -1883,6 +1885,11 @@ static void emitCallToBadCast(CIRGenFunction &cgf, mlir::Location loc) {
cgf.getBuilder().clearInsertionPoint();
}
+void CIRGenItaniumCXXABI::emitBadCastCall(CIRGenFunction &cgf,
+ mlir::Location loc) {
+ emitCallToBadCast(cgf, loc);
+}
+
// TODO(cir): This could be shared with classic codegen.
static CharUnits computeOffsetHint(ASTContext &astContext,
const CXXRecordDecl *src,
diff --git a/clang/test/CIR/CodeGen/dynamic-cast-exact.cpp b/clang/test/CIR/CodeGen/dynamic-cast-exact.cpp
index e3b853321ac7d..9ddb68fbf7355 100644
--- a/clang/test/CIR/CodeGen/dynamic-cast-exact.cpp
+++ b/clang/test/CIR/CodeGen/dynamic-cast-exact.cpp
@@ -172,3 +172,37 @@ B *offset_cast(A *a) {
// OGCG-NEXT: br label %[[LABEL_END]]
// OGCG: [[LABEL_END]]:
// OGCG-NEXT: phi ptr [ %[[RESULT]], %[[LABEL_NOTNULL]] ], [ null, %[[LABEL_NULL]] ]
+
+Derived *ptr_cast_always_fail(Base2 *ptr) {
+ return dynamic_cast<Derived *>(ptr);
+ }
+
+// CIR: cir.func {{.*}} @_Z20ptr_cast_always_failP5Base2
+// CIR: %{{.+}} = cir.load{{.*}} %{{.+}} : !cir.ptr<!cir.ptr<!rec_Base2>>, !cir.ptr<!rec_Base2>
+// CIR-NEXT: %[[RESULT:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!rec_Derived>
+// CIR-NEXT: cir.store %[[RESULT]], %{{.*}} : !cir.ptr<!rec_Derived>, !cir.ptr<!cir.ptr<!rec_Derived>>
+
+// LLVM: define {{.*}} ptr @_Z20ptr_cast_always_failP5Base2
+// LLVM-NEXT: ret ptr null
+
+// OGCG: define {{.*}} ptr @_Z20ptr_cast_always_failP5Base2
+// OGCG-NEXT: entry:
+// OGCG-NEXT: ret ptr null
+
+Derived &ref_cast_always_fail(Base2 &ref) {
+ return dynamic_cast<Derived &>(ref);
+}
+
+// CIR: cir.func {{.*}} @_Z20ref_cast_always_failR5Base2
+// CIR: %{{.+}} = cir.load{{.*}} %{{.+}} : !cir.ptr<!cir.ptr<!rec_Base2>>, !cir.ptr<!rec_Base2>
+// CIR-NEXT: cir.call @__cxa_bad_cast() : () -> ()
+// CIR-NEXT: cir.unreachable
+
+// LLVM: define {{.*}} ptr @_Z20ref_cast_always_failR5Base2
+// LLVM-NEXT: tail call void @__cxa_bad_cast()
+// LLVM-NEXT: unreachable
+
+// OGCG: define {{.*}} ptr @_Z20ref_cast_always_failR5Base2
+// OGCG-NEXT: entry:
+// OGCG-NEXT: tail call void @__cxa_bad_cast()
+// OGCG-NEXT: unreachable
More information about the cfe-commits
mailing list