diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index e44749672d582..0c447b20cef40 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -3952,9 +3952,20 @@ bool CodeGenModule::shouldEmitFunction(GlobalDecl GD) { // behavior may break ABI compatibility of the current unit. if (const Module *M = F->getOwningModule(); M && M->getTopLevelModule()->isNamedModule() && - getContext().getCurrentNamedModule() != M->getTopLevelModule() && - !F->hasAttr()) - return false; + getContext().getCurrentNamedModule() != M->getTopLevelModule()) { + // There are practices to mark template member function as always-inline + // and mark the template as extern explicit instantiation but not give + // the definition for member function. So we have to emit the function + // from explicitly instantiation with always-inline. + // + // See https://github.com/llvm/llvm-project/issues/86893 for details. + // + // TODO: Maybe it is better to give it a warning if we call a non-inline + // function from other module units which is marked as always-inline. + if (!F->isTemplateInstantiation() || !F->hasAttr()) { + return false; + } + } if (F->hasAttr()) return false; diff --git a/clang/test/CodeGenCXX/module-funcs-from-imports.cppm b/clang/test/CodeGenCXX/module-funcs-from-imports.cppm index 33cdf437110a9..a2a9122fc3913 100644 --- a/clang/test/CodeGenCXX/module-funcs-from-imports.cppm +++ b/clang/test/CodeGenCXX/module-funcs-from-imports.cppm @@ -23,6 +23,21 @@ int func_in_gmf_not_called() { return 44; } +template +class A { +public: + __attribute__((always_inline)) + inline constexpr int getValue() { + return 43; + } + + inline constexpr int getValue2() { + return 43; + } +}; + +extern template class A; + //--- M.cppm module; #include "foo.h" @@ -47,17 +62,21 @@ int always_inline_func() { return 45; } +export using ::A; + //--- Use.cpp import M; int use() { - return exported_func() + always_inline_func(); + A a; + return exported_func() + always_inline_func() + + a.getValue() + a.getValue2(); } -// Checks that none of the function (except the always_inline_func) in the importees -// are generated in the importer's code. // CHECK-O0: define{{.*}}_Z3usev( // CHECK-O0: declare{{.*}}_ZW1M13exported_funcv( -// CHECK-O0: define{{.*}}available_externally{{.*}}_ZW1M18always_inline_funcv( +// CHECK-O0: declare{{.*}}_ZW1M18always_inline_funcv( +// CHECK-O0: define{{.*}}@_ZN1AIcE8getValueEv( +// CHECK-O0: declare{{.*}}@_ZN1AIcE9getValue2Ev( // CHECK-O0-NOT: func_in_gmf // CHECK-O0-NOT: func_in_gmf_not_called // CHECK-O0-NOT: non_exported_func @@ -68,7 +87,9 @@ int use() { // O0 to keep consistent ABI. // CHECK-O1: define{{.*}}_Z3usev( // CHECK-O1: declare{{.*}}_ZW1M13exported_funcv( -// CHECK-O1: define{{.*}}available_externally{{.*}}_ZW1M18always_inline_funcv( +// CHECK-O1: declare{{.*}}_ZW1M18always_inline_funcv( +// CHECK-O1: define{{.*}}@_ZN1AIcE8getValueEv( +// CHECK-O1: declare{{.*}}@_ZN1AIcE9getValue2Ev( // CHECK-O1-NOT: func_in_gmf // CHECK-O1-NOT: func_in_gmf_not_called // CHECK-O1-NOT: non_exported_func