mirror of
https://github.com/intel/llvm.git
synced 2026-01-27 06:06:34 +08:00
[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:
@@ -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;
|
||||
|
||||
37
clang/test/Modules/expose-static-inline-from-gmf-1.cppm
Normal file
37
clang/test/Modules/expose-static-inline-from-gmf-1.cppm
Normal 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
|
||||
22
clang/test/Modules/expose-static-inline-from-gmf-2.cppm
Normal file
22
clang/test/Modules/expose-static-inline-from-gmf-2.cppm
Normal 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
|
||||
24
clang/test/Modules/expose-static-inline-from-gmf-3.cppm
Normal file
24
clang/test/Modules/expose-static-inline-from-gmf-3.cppm
Normal 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
|
||||
40
clang/test/Modules/expose-static-inline-from-gmf-4.cppm
Normal file
40
clang/test/Modules/expose-static-inline-from-gmf-4.cppm
Normal 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
|
||||
26
clang/test/Modules/expose-static-inline-from-gmf-5.cppm
Normal file
26
clang/test/Modules/expose-static-inline-from-gmf-5.cppm
Normal 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
|
||||
Reference in New Issue
Block a user