https://github.com/pfusik created 
https://github.com/llvm/llvm-project/pull/207349

This is a follow-up to #202594, which fixed a pointer cast, but not
a reference cast. Surprisingly, `CXXDynamicCastExpr::getType()`
for a reference cast is a `RecordType` and not a `ReferenceType`.

How this happens:
In `Sema::BuildCXXNamedCast`, a `CastOperation Op` variable is constructed.
The `CastOperation` constructor initializes
`ResultType(destType.getNonLValueExprType(S.Context))`
where `QualType::getNonLValueExprType` turns a `ReferenceType` into
a `RecordType`. `Sema::BuildCXXNamedCast` then passes `Op.ResultType`
to `CXXDynamicCastExpr::Create`.

>From ac90b2c07859d1f8b745da98eff11ad9e07971e1 Mon Sep 17 00:00:00 2001
From: Piotr Fusik <[email protected]>
Date: Fri, 3 Jul 2026 10:14:56 +0200
Subject: [PATCH 1/2] [Clang] Fix missing vtable for `dynamic_cast<FinalClass
 &>(*this)` in a function template

This is a follow-up to #202594, which fixed a pointer cast, but not
a reference cast. Surprisingly, `CXXDynamicCastExpr::getType()`
for a reference cast is a `RecordType` and not a `ReferenceType`.

How this happens:
In `Sema::BuildCXXNamedCast`, a `CastOperation Op` variable is constructed.
The `CastOperation` constructor initializes
`ResultType(destType.getNonLValueExprType(S.Context))`
where `QualType::getNonLValueExprType` turns a `ReferenceType` into
a `RecordType`. `Sema::BuildCXXNamedCast` then passes `Op.ResultType`
to `CXXDynamicCastExpr::Create`.
---
 clang/lib/Sema/SemaTemplateInstantiate.cpp   |  5 ++++-
 clang/test/CodeGenCXX/dynamic-cast-exact.cpp | 14 ++++++++++++++
 2 files changed, 18 insertions(+), 1 deletion(-)

diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp 
b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index a77ea5fd3dfff..4ba9414cfcdad 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -1891,7 +1891,10 @@ namespace {
       ExprResult Ret = inherited::TransformCXXDynamicCastExpr(E);
       if (Ret.isInvalid())
         return Ret;
-      auto *DestDecl = Ret.get()->getType()->getPointeeCXXRecordDecl();
+      QualType T = Ret.get()->getType();
+      if (const auto *PT = T->getAsCanonical<PointerType>())
+        T = PT->getPointeeType();
+      auto *DestDecl = T->getAsCXXRecordDecl();
       if (DestDecl && DestDecl->isEffectivelyFinal())
         getSema().MarkVTableUsed(Ret.get()->getExprLoc(), DestDecl);
       return Ret;
diff --git a/clang/test/CodeGenCXX/dynamic-cast-exact.cpp 
b/clang/test/CodeGenCXX/dynamic-cast-exact.cpp
index 86a97f764e729..7bf701206f5a4 100644
--- a/clang/test/CodeGenCXX/dynamic-cast-exact.cpp
+++ b/clang/test/CodeGenCXX/dynamic-cast-exact.cpp
@@ -139,3 +139,17 @@ namespace GH198511 {
   template<class T> B *A::cast() { return dynamic_cast<B*>(this); }
   template B *A::cast<int>();
 }
+
+namespace GH198511Ref {
+  // Ensure we mark the B vtable as used here, because we're going to emit a
+  // reference to it.
+  // CHECK: define {{.*}} @_ZN11GH198511Ref1BD0
+  struct B;
+  struct A {
+    virtual ~A() = default;
+    template<class T> B &cast();
+  };
+  struct B final : A { };
+  template<class T> B &A::cast() { return dynamic_cast<B&>(*this); }
+  template B &A::cast<int>();
+}

>From 5f27c06c894296cd7f86ebfa9c26a5dd8ed7403c Mon Sep 17 00:00:00 2001
From: Piotr Fusik <[email protected]>
Date: Fri, 3 Jul 2026 10:19:10 +0200
Subject: [PATCH 2/2] [Clang][NFC] Revert the return type change from #202594

This function isn't used for the fix now.
---
 clang/include/clang/AST/TypeBase.h | 2 +-
 clang/lib/AST/Type.cpp             | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/include/clang/AST/TypeBase.h 
b/clang/include/clang/AST/TypeBase.h
index 3a801e2857b13..c9658775f0470 100644
--- a/clang/include/clang/AST/TypeBase.h
+++ b/clang/include/clang/AST/TypeBase.h
@@ -2950,7 +2950,7 @@ class alignas(TypeAlignment) Type : public 
ExtQualsTypeCommonBase {
   ///
   /// If this is not a pointer or reference, or the type being pointed to does
   /// not refer to a CXXRecordDecl, returns NULL.
-  CXXRecordDecl *getPointeeCXXRecordDecl() const;
+  const CXXRecordDecl *getPointeeCXXRecordDecl() const;
 
   /// Get the DeducedType whose type will be deduced for a variable with
   /// an initializer of this type. This looks through declarators like pointer
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 19b85d0e0af69..42d148715bc40 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -1955,7 +1955,7 @@ const ObjCObjectPointerType 
*Type::getAsObjCInterfacePointerType() const {
   return nullptr;
 }
 
-CXXRecordDecl *Type::getPointeeCXXRecordDecl() const {
+const CXXRecordDecl *Type::getPointeeCXXRecordDecl() const {
   QualType PointeeType;
   if (const auto *PT = getAsCanonical<PointerType>())
     PointeeType = PT->getPointeeType();

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to