https://github.com/pfusik updated 
https://github.com/llvm/llvm-project/pull/202594

>From ec15da9e59d10e219dabcdb53580b5f48bac352a Mon Sep 17 00:00:00 2001
From: Piotr Fusik <[email protected]>
Date: Tue, 9 Jun 2026 14:38:34 +0200
Subject: [PATCH 1/3] [Clang] Fix missing vtable for `dynamic_cast<FinalClass
 *>(this)` in a function template

9d525bf94b255df89587db955b5fa2d3c03c2c3e introduced an optimization
of `dynamic_cast<FinalClass *>` by comparing vtables. This requires
the vtable to be emitted, which was fixed for most cases in #64088.

This change addresses a missing case of a `dynamic_cast` of `this`
in a function template. We ensure that `Sema::MarkVTableUsed`
gets called during template instantiation. It wasn't because
`CXXThisExpr` is unchanged by template instantiation.

Fix #198511
---
 clang/lib/Sema/TreeTransform.h               |  5 ++---
 clang/test/CodeGenCXX/dynamic-cast-exact.cpp | 14 ++++++++++++++
 2 files changed, 16 insertions(+), 3 deletions(-)

diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 77c8f17439a1a..80dc15cc5a001 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -14555,9 +14555,8 @@ 
TreeTransform<Derived>::TransformCXXNamedCastExpr(CXXNamedCastExpr *E) {
   if (SubExpr.isInvalid())
     return ExprError();
 
-  if (!getDerived().AlwaysRebuild() &&
-      Type == E->getTypeInfoAsWritten() &&
-      SubExpr.get() == E->getSubExpr())
+  if (!getDerived().AlwaysRebuild() && Type == E->getTypeInfoAsWritten() &&
+      SubExpr.get() == E->getSubExpr() && !isa<CXXThisExpr>(SubExpr.get()))
     return E;
   return getDerived().RebuildCXXNamedCastExpr(
       E->getOperatorLoc(), E->getStmtClass(), E->getAngleBrackets().getBegin(),
diff --git a/clang/test/CodeGenCXX/dynamic-cast-exact.cpp 
b/clang/test/CodeGenCXX/dynamic-cast-exact.cpp
index 588d80844a2fa..86a97f764e729 100644
--- a/clang/test/CodeGenCXX/dynamic-cast-exact.cpp
+++ b/clang/test/CodeGenCXX/dynamic-cast-exact.cpp
@@ -125,3 +125,17 @@ namespace GH64088 {
   struct B final : A { virtual ~B() = default; };
   B *cast(A *p) { return dynamic_cast<B*>(p); }
 }
+
+namespace GH198511 {
+  // Ensure we mark the B vtable as used here, because we're going to emit a
+  // reference to it.
+  // CHECK: define {{.*}} @_ZN8GH1985111BD0
+  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 cdda943d6cac30c7acd681259e2502765f1564d7 Mon Sep 17 00:00:00 2001
From: Piotr Fusik <[email protected]>
Date: Wed, 10 Jun 2026 12:27:29 +0200
Subject: [PATCH 2/3] [Clang] Move the fix to `TemplateInstantiator`

---
 clang/lib/Sema/SemaTemplateInstantiate.cpp | 13 +++++++++++++
 clang/lib/Sema/TreeTransform.h             |  5 +++--
 2 files changed, 16 insertions(+), 2 deletions(-)

diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp 
b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 6df6d5505c61c..e42bc762a5c37 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -1652,6 +1652,19 @@ namespace {
         SmallVectorImpl<QualType> &PTypes,
         SmallVectorImpl<ParmVarDecl *> &TransParams,
         Sema::ExtParameterInfoBuilder &PInfos);
+
+    ExprResult TransformCXXDynamicCastExpr(CXXDynamicCastExpr *E) {
+      ExprResult Ret = inherited::TransformCXXDynamicCastExpr(E);
+      if (Ret.isInvalid())
+        return Ret;
+      if (const auto *DestDecl =
+              Ret.get()->getType()->getPointeeCXXRecordDecl()) {
+        if (DestDecl->isEffectivelyFinal())
+          getSema().MarkVTableUsed(Ret.get()->getExprLoc(),
+                                   const_cast<CXXRecordDecl *>(DestDecl));
+      }
+      return Ret;
+    }
   };
 }
 
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 80dc15cc5a001..77c8f17439a1a 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -14555,8 +14555,9 @@ 
TreeTransform<Derived>::TransformCXXNamedCastExpr(CXXNamedCastExpr *E) {
   if (SubExpr.isInvalid())
     return ExprError();
 
-  if (!getDerived().AlwaysRebuild() && Type == E->getTypeInfoAsWritten() &&
-      SubExpr.get() == E->getSubExpr() && !isa<CXXThisExpr>(SubExpr.get()))
+  if (!getDerived().AlwaysRebuild() &&
+      Type == E->getTypeInfoAsWritten() &&
+      SubExpr.get() == E->getSubExpr())
     return E;
   return getDerived().RebuildCXXNamedCastExpr(
       E->getOperatorLoc(), E->getStmtClass(), E->getAngleBrackets().getBegin(),

>From 8d4730f8e32801aefab02b864897452ed45c731d Mon Sep 17 00:00:00 2001
From: Piotr Fusik <[email protected]>
Date: Thu, 11 Jun 2026 10:14:08 +0200
Subject: [PATCH 3/3] [Clang][NFC] Avoid `const_cast` by changing the return
 type of `Type::getPointeeCXXRecordDecl`

---
 clang/include/clang/AST/TypeBase.h         | 2 +-
 clang/lib/AST/Type.cpp                     | 2 +-
 clang/lib/Sema/SemaTemplateInstantiate.cpp | 6 ++----
 3 files changed, 4 insertions(+), 6 deletions(-)

diff --git a/clang/include/clang/AST/TypeBase.h 
b/clang/include/clang/AST/TypeBase.h
index e3141a82b54d2..41b7a0e22b4d9 100644
--- a/clang/include/clang/AST/TypeBase.h
+++ b/clang/include/clang/AST/TypeBase.h
@@ -2948,7 +2948,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.
-  const CXXRecordDecl *getPointeeCXXRecordDecl() 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 55c3e5c3ead17..13cbef3401874 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -1956,7 +1956,7 @@ const ObjCObjectPointerType 
*Type::getAsObjCInterfacePointerType() const {
   return nullptr;
 }
 
-const CXXRecordDecl *Type::getPointeeCXXRecordDecl() const {
+CXXRecordDecl *Type::getPointeeCXXRecordDecl() const {
   QualType PointeeType;
   if (const auto *PT = getAsCanonical<PointerType>())
     PointeeType = PT->getPointeeType();
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp 
b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index e42bc762a5c37..d6463a57721a3 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -1657,11 +1657,9 @@ namespace {
       ExprResult Ret = inherited::TransformCXXDynamicCastExpr(E);
       if (Ret.isInvalid())
         return Ret;
-      if (const auto *DestDecl =
-              Ret.get()->getType()->getPointeeCXXRecordDecl()) {
+      if (auto *DestDecl = Ret.get()->getType()->getPointeeCXXRecordDecl()) {
         if (DestDecl->isEffectivelyFinal())
-          getSema().MarkVTableUsed(Ret.get()->getExprLoc(),
-                                   const_cast<CXXRecordDecl *>(DestDecl));
+          getSema().MarkVTableUsed(Ret.get()->getExprLoc(), DestDecl);
       }
       return Ret;
     }

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

Reply via email to