diff --git a/clang/lib/Sema/SemaCast.cpp b/clang/lib/Sema/SemaCast.cpp index d65ecf52c5231..b338d601db739 100644 --- a/clang/lib/Sema/SemaCast.cpp +++ b/clang/lib/Sema/SemaCast.cpp @@ -935,6 +935,14 @@ void CastOperation::CheckDynamicCast() { << isClangCL; } + // For a dynamic_cast to a final type, IR generation might emit a reference + // to the vtable. + if (DestRecord) { + auto *DestDecl = DestRecord->getAsCXXRecordDecl(); + if (DestDecl->isEffectivelyFinal()) + Self.MarkVTableUsed(OpRange.getBegin(), DestDecl); + } + // Done. Everything else is run-time checks. Kind = CK_Dynamic; } diff --git a/clang/test/CodeGenCXX/dynamic-cast-exact.cpp b/clang/test/CodeGenCXX/dynamic-cast-exact.cpp index 676aa975a7268..bd283e85101b4 100644 --- a/clang/test/CodeGenCXX/dynamic-cast-exact.cpp +++ b/clang/test/CodeGenCXX/dynamic-cast-exact.cpp @@ -76,3 +76,12 @@ H *exact_multi(A *a) { // CHECK: phi ptr [ %[[RESULT]], %[[LABEL_NOTNULL]] ], [ null, %[[LABEL_FAILED]] ] return dynamic_cast(a); } + +namespace GH64088 { + // Ensure we mark the B vtable as used here, because we're going to emit a + // reference to it. + // CHECK: define {{.*}} @_ZN7GH640881BD0 + struct A { virtual ~A(); }; + struct B final : A { virtual ~B() = default; }; + B *cast(A *p) { return dynamic_cast(p); } +}