[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>.
This commit is contained in:
Jan Kokemüller
2024-12-31 02:53:29 +01:00
committed by GitHub
parent fe1f64e7e9
commit e50ec3e46b
6 changed files with 167 additions and 3 deletions

View File

@@ -6977,11 +6977,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();
IsImplicitlyInstantiated = SpecInfo->getTemplateSpecializationKind() ==
TSK_ImplicitInstantiation;
}
if (ND->getFormalLinkage() == Linkage::Internal) {
/// 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;

View File

@@ -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-note@test.cc:2 {{in instantiation of function template specialization 'a<long>' requested here}}
#endif

View File

@@ -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

View File

@@ -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

View File

@@ -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-note@test.cc:2 {{in instantiation of function template specialization 'ns::a<long>' requested here}}
#endif

View File

@@ -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