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
