llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang @llvm/pr-subscribers-clang-modules Author: Jan Kokemüller (jiixyj) <details> <summary>Changes</summary> 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>. --- Full diff: https://github.com/llvm/llvm-project/pull/104701.diff 6 Files Affected: - (modified) clang/lib/Sema/SemaOverload.cpp (+19-4) - (added) clang/test/Modules/expose-static-inline-from-gmf-1.cppm (+37) - (added) clang/test/Modules/expose-static-inline-from-gmf-2.cppm (+22) - (added) clang/test/Modules/expose-static-inline-from-gmf-3.cppm (+24) - (added) clang/test/Modules/expose-static-inline-from-gmf-4.cppm (+40) - (added) clang/test/Modules/expose-static-inline-from-gmf-5.cppm (+26) ``````````diff diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 52f640eb96b73b..ca070d0e22d472 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -6926,11 +6926,26 @@ void Sema::AddOverloadCandidate( /// have linkage. So that all entities of the same should share one /// linkage. But in clang, different entities of the same could have /// different 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. + /// The global module is meant to be a transition mechanism for C and C++ + /// headers. + /// Inline functions with internal linkage are a common pattern in headers + /// to avoid ODR issues. + const bool IsInlineFunctionInGMF = + Function->getOwningModule() && + Function->getOwningModule()->isGlobalModule() && + (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 `````````` </details> https://github.com/llvm/llvm-project/pull/104701 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits