Index: clang/lib/CodeGen/CGVTables.cpp =================================================================== --- clang/lib/CodeGen/CGVTables.cpp +++ clang/lib/CodeGen/CGVTables.cpp @@ -1174,7 +1174,24 @@ // Otherwise, if we don't have a definition of the key function, the // vtable must be defined somewhere else. - return !keyFunction->hasBody(); + if (!keyFunction->hasBody()) + return true; + + // If the non-inline key function comes from another module unit, the vtable + // must be defined there. + if (keyFunction->isInAnotherModuleUnit()) { + bool AllowInlineKeyFunctions = + CGM.getContext().getTargetInfo().getCXXABI().canKeyFunctionBeInline(); + const FunctionDecl *Def; + bool InlineKeyFunctions = AllowInlineKeyFunctions && + keyFunction->hasBody(Def) && + Def->isInlineSpecified(); + + if (!InlineKeyFunctions) + return true; + } + + return false; } /// Given that we're currently at the end of the translation unit, and Index: clang/test/CodeGenCXX/modules-vtable.cppm =================================================================== --- /dev/null +++ clang/test/CodeGenCXX/modules-vtable.cppm @@ -0,0 +1,60 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: cd %t +// +// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 -emit-module-interface \ +// RUN: %t/Mod.cppm -o %t/Mod.pcm +// +// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 %t/Mod.pcm \ +// RUN: -emit-llvm -o - | FileCheck %t/Mod.cppm +// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 -fmodule-file=Mod=%t/Mod.pcm \ +// RUN: %t/Use.cpp -emit-llvm -o - | FileCheck %t/Use.cpp +// +// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 -emit-module-interface \ +// RUN: %t/Mod.cppm -o %t/Mod.pcm -DKEY_FUNCTION_INLINE +// +// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 %t/Mod.pcm \ +// RUN: -emit-llvm -o - | FileCheck %t/Mod.cppm -check-prefix=CHECK-INLINE +// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 -fmodule-file=Mod=%t/Mod.pcm \ +// RUN: %t/Use.cpp -emit-llvm -o - | FileCheck %t/Use.cpp -check-prefix=CHECK-INLINE + +//--- Mod.cppm +export module Mod; + +export class Base { +public: + virtual ~Base(); +}; +#ifdef KEY_FUNCTION_INLINE +inline +#endif +Base::~Base() {} + +// CHECK: @_ZTVW3Mod4Base = unnamed_addr constant +// CHECK: @_ZTSW3Mod4Base = constant +// CHECK: @_ZTIW3Mod4Base = constant + +// CHECK-INLINE: @_ZTVW3Mod4Base = linkonce_odr unnamed_addr constant +// CHECK-INLINE: @_ZTSW3Mod4Base = linkonce_odr constant +// CHECK-INLINE: @_ZTIW3Mod4Base = linkonce_odr constant + +module :private; +int private_use() { + Base base; + return 43; +} + +//--- Use.cpp +import Mod; +int use() { + Base* base = new Base(); + return 43; +} + +// CHECK-NOT: @_ZTSW3Mod4Base = constant +// CHECK-NOT: @_ZTIW3Mod4Base = constant +// CHECK: @_ZTVW3Mod4Base = external unnamed_addr + +// CHECK-INLINE: @_ZTVW3Mod4Base = linkonce_odr unnamed_addr constant +// CHECK-INLINE: @_ZTSW3Mod4Base = linkonce_odr constant +// CHECK-INLINE: @_ZTIW3Mod4Base = linkonce_odr constant