Author: Jan Kokemüller Date: 2024-12-31T09:53:29+08:00 New Revision: e50ec3e46bea819a1d7aea1cee2d7e11197bbdd2
URL: https://github.com/llvm/llvm-project/commit/e50ec3e46bea819a1d7aea1cee2d7e11197bbdd2 DIFF: https://github.com/llvm/llvm-project/commit/e50ec3e46bea819a1d7aea1cee2d7e11197bbdd2.diff LOG: [Clang][Sema] Expose static inline functions from GMF (#104701) In C, it is a common pattern to have `static inline` functions in headers to avoid ODR issues. Currently, when those headers are included in a GMF, the names are not found when two-phase name lookup and ADL is involved. Those names are removed by `Sema::AddOverloadCandidate`. Similarly, in C++, sometimes people use templates with internal linkage in headers. As the GMF was designed to be a transitional mechanism for headers, special case those functions in `Sema::AddOverloadCandidate`. This fixes <https://github.com/llvm/llvm-project/issues/98021>. Added: clang/test/Modules/expose-static-inline-from-gmf-1.cppm clang/test/Modules/expose-static-inline-from-gmf-2.cppm clang/test/Modules/expose-static-inline-from-gmf-3.cppm clang/test/Modules/expose-static-inline-from-gmf-4.cppm clang/test/Modules/expose-static-inline-from-gmf-5.cppm Modified: clang/lib/Sema/SemaOverload.cpp Removed: ################################################################################ diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index fff49b759c935e..7589701fb81de9 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -6977,11 +6977,26 @@ void Sema::AddOverloadCandidate( /// have linkage. So that all entities of the same should share one /// linkage. But in clang, diff erent entities of the same could have /// diff erent linkage. - NamedDecl *ND = Function; - if (auto *SpecInfo = Function->getTemplateSpecializationInfo()) + const NamedDecl *ND = Function; + bool IsImplicitlyInstantiated = false; + if (auto *SpecInfo = Function->getTemplateSpecializationInfo()) { ND = SpecInfo->getTemplate(); - - if (ND->getFormalLinkage() == Linkage::Internal) { + IsImplicitlyInstantiated = SpecInfo->getTemplateSpecializationKind() == + TSK_ImplicitInstantiation; + } + + /// Don't remove inline functions with internal linkage from the overload + /// set if they are declared in a GMF, in violation of C++ [basic.link]p17. + /// However: + /// - Inline functions with internal linkage are a common pattern in + /// headers to avoid ODR issues. + /// - The global module is meant to be a transition mechanism for C and C++ + /// headers, and the current rules as written work against that goal. + const bool IsInlineFunctionInGMF = + Function->isFromGlobalModule() && + (IsImplicitlyInstantiated || Function->isInlined()); + + if (ND->getFormalLinkage() == Linkage::Internal && !IsInlineFunctionInGMF) { Candidate.Viable = false; Candidate.FailureKind = ovl_fail_module_mismatched; return; diff --git a/clang/test/Modules/expose-static-inline-from-gmf-1.cppm b/clang/test/Modules/expose-static-inline-from-gmf-1.cppm new file mode 100644 index 00000000000000..4de9b583dac8da --- /dev/null +++ b/clang/test/Modules/expose-static-inline-from-gmf-1.cppm @@ -0,0 +1,37 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: split-file %s %t +// +// RUN: %clang -std=c++20 %t/a.cppm --precompile -o %t/a.pcm \ +// RUN: -DTEST_INLINE +// RUN: %clang -std=c++20 %t/test.cc -fprebuilt-module-path=%t -fsyntax-only -Xclang -verify \ +// RUN: -DTEST_INLINE +// +// RUN: %clang -std=c++20 %t/a.cppm --precompile -o %t/a.pcm +// RUN: %clang -std=c++20 %t/test.cc -fprebuilt-module-path=%t -fsyntax-only -Xclang -verify + +//--- a.h +#ifdef TEST_INLINE +#define INLINE inline +#else +#define INLINE +#endif +static INLINE void func(long) {} +template <typename T = long> void a() { func(T{}); } + +//--- a.cppm +module; +#include "a.h" +export module a; +export using ::a; + +//--- test.cc +import a; +auto m = (a(), 0); + +#ifdef TEST_INLINE +// expected-no-diagnostics +#else +// expected-error@a.h:7 {{no matching function for call to 'func'}} +// expected-n...@test.cc:2 {{in instantiation of function template specialization 'a<long>' requested here}} +#endif diff --git a/clang/test/Modules/expose-static-inline-from-gmf-2.cppm b/clang/test/Modules/expose-static-inline-from-gmf-2.cppm new file mode 100644 index 00000000000000..c89b613f5074b1 --- /dev/null +++ b/clang/test/Modules/expose-static-inline-from-gmf-2.cppm @@ -0,0 +1,22 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: split-file %s %t +// +// RUN: %clang -std=c++20 %t/a.cppm --precompile -o %t/a.pcm +// RUN: %clang -std=c++20 %t/test.cc -fprebuilt-module-path=%t -fsyntax-only -Xclang -verify + +//--- a.h +template <typename G> static inline void func() {} +template <typename T = long> void a() { func<T>(); } + +//--- a.cppm +module; +#include "a.h" +export module a; +export using ::a; + +//--- test.cc +import a; +auto m = (a(), 0); + +// expected-no-diagnostics diff --git a/clang/test/Modules/expose-static-inline-from-gmf-3.cppm b/clang/test/Modules/expose-static-inline-from-gmf-3.cppm new file mode 100644 index 00000000000000..dee7cddafdf701 --- /dev/null +++ b/clang/test/Modules/expose-static-inline-from-gmf-3.cppm @@ -0,0 +1,24 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: split-file %s %t +// +// RUN: %clang -std=c++20 %t/a.cppm --precompile -o %t/a.pcm +// RUN: %clang -std=c++20 %t/test.cc -fprebuilt-module-path=%t -fsyntax-only -Xclang -verify + +//--- a.h +namespace ns { +template <typename G> static void func() {} +template <typename T = long> void a() { func<T>(); } +} + +//--- a.cppm +module; +#include "a.h" +export module a; +export using ns::a; + +//--- test.cc +import a; +auto m = (a(), 0); + +// expected-no-diagnostics diff --git a/clang/test/Modules/expose-static-inline-from-gmf-4.cppm b/clang/test/Modules/expose-static-inline-from-gmf-4.cppm new file mode 100644 index 00000000000000..09c6b1ffd9c797 --- /dev/null +++ b/clang/test/Modules/expose-static-inline-from-gmf-4.cppm @@ -0,0 +1,40 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: split-file %s %t +// +// RUN: %clang -std=c++20 %t/a.cppm --precompile -o %t/a.pcm \ +// RUN: -DTEST_INLINE +// RUN: %clang -std=c++20 %t/test.cc -fprebuilt-module-path=%t -fsyntax-only -Xclang -verify \ +// RUN: -DTEST_INLINE +// +// RUN: %clang -std=c++20 %t/a.cppm --precompile -o %t/a.pcm +// RUN: %clang -std=c++20 %t/test.cc -fprebuilt-module-path=%t -fsyntax-only -Xclang -verify + +//--- a.h +#ifdef TEST_INLINE +#define INLINE inline +#else +#define INLINE +#endif +namespace ns { +template <typename G> static void func() {} +template <> INLINE void func<long>() {} +template <typename T = long> void a() { func<T>(); } +} + +//--- a.cppm +module; +#include "a.h" +export module a; +export using ns::a; + +//--- test.cc +import a; +auto m = (a(), 0); + +#ifdef TEST_INLINE +// expected-no-diagnostics +#else +// expected-error@a.h:9 {{no matching function for call to 'func'}} +// expected-n...@test.cc:2 {{in instantiation of function template specialization 'ns::a<long>' requested here}} +#endif diff --git a/clang/test/Modules/expose-static-inline-from-gmf-5.cppm b/clang/test/Modules/expose-static-inline-from-gmf-5.cppm new file mode 100644 index 00000000000000..334af845a693dd --- /dev/null +++ b/clang/test/Modules/expose-static-inline-from-gmf-5.cppm @@ -0,0 +1,26 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: split-file %s %t +// +// RUN: %clang -std=c++20 %t/a.cppm --precompile -o %t/a.pcm +// RUN: %clang -std=c++20 %t/test.cc -fprebuilt-module-path=%t -fsyntax-only -Xclang -verify + +//--- a.h +namespace ns { +namespace { +template <typename G> void func() {} +} +template <typename T = long> void a() { func<T>(); } +} + +//--- a.cppm +module; +#include "a.h" +export module a; +export using ns::a; + +//--- test.cc +import a; +auto m = (a(), 0); + +// expected-no-diagnostics _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits