Skip to content

Conversation

@andykaylor
Copy link
Contributor

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.

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.
@llvmbot llvmbot added clang Clang issues not falling into any other category ClangIR Anything related to the ClangIR project labels Oct 22, 2025
@llvmbot
Copy link
Member

llvmbot commented Oct 22, 2025

@llvm/pr-subscribers-clang

@llvm/pr-subscribers-clangir

Author: Andy Kaylor (andykaylor)

Changes

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.


Full diff: https://github.com/llvm/llvm-project/pull/164732.diff

4 Files Affected:

  • (modified) clang/lib/CIR/CodeGen/CIRGenCXXABI.h (+2)
  • (modified) clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp (+22-4)
  • (modified) clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp (+7)
  • (modified) clang/test/CIR/CodeGen/dynamic-cast-exact.cpp (+34)
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

Copy link
Member

@Lancern Lancern left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@andykaylor andykaylor merged commit ede9dd3 into llvm:main Oct 23, 2025
13 checks passed
@andykaylor andykaylor deleted the cir-dyncast-null branch October 23, 2025 17:01
dvbuka pushed a commit to dvbuka/llvm-project that referenced this pull request Oct 27, 2025
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.
Lukacma pushed a commit to Lukacma/llvm-project that referenced this pull request Oct 29, 2025
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.
aokblast pushed a commit to aokblast/llvm-project that referenced this pull request Oct 30, 2025
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang Clang issues not falling into any other category ClangIR Anything related to the ClangIR project

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants