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
