Skip to content

Commit e50ec3e

Browse files
authored
[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 <#98021>.
1 parent fe1f64e commit e50ec3e

6 files changed

+168
-4
lines changed

clang/lib/Sema/SemaOverload.cpp

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6977,11 +6977,26 @@ void Sema::AddOverloadCandidate(
69776977
/// have linkage. So that all entities of the same should share one
69786978
/// linkage. But in clang, different entities of the same could have
69796979
/// different linkage.
6980-
NamedDecl *ND = Function;
6981-
if (auto *SpecInfo = Function->getTemplateSpecializationInfo())
6980+
const NamedDecl *ND = Function;
6981+
bool IsImplicitlyInstantiated = false;
6982+
if (auto *SpecInfo = Function->getTemplateSpecializationInfo()) {
69826983
ND = SpecInfo->getTemplate();
6983-
6984-
if (ND->getFormalLinkage() == Linkage::Internal) {
6984+
IsImplicitlyInstantiated = SpecInfo->getTemplateSpecializationKind() ==
6985+
TSK_ImplicitInstantiation;
6986+
}
6987+
6988+
/// Don't remove inline functions with internal linkage from the overload
6989+
/// set if they are declared in a GMF, in violation of C++ [basic.link]p17.
6990+
/// However:
6991+
/// - Inline functions with internal linkage are a common pattern in
6992+
/// headers to avoid ODR issues.
6993+
/// - The global module is meant to be a transition mechanism for C and C++
6994+
/// headers, and the current rules as written work against that goal.
6995+
const bool IsInlineFunctionInGMF =
6996+
Function->isFromGlobalModule() &&
6997+
(IsImplicitlyInstantiated || Function->isInlined());
6998+
6999+
if (ND->getFormalLinkage() == Linkage::Internal && !IsInlineFunctionInGMF) {
69857000
Candidate.Viable = false;
69867001
Candidate.FailureKind = ovl_fail_module_mismatched;
69877002
return;
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// RUN: rm -rf %t
2+
// RUN: mkdir -p %t
3+
// RUN: split-file %s %t
4+
//
5+
// RUN: %clang -std=c++20 %t/a.cppm --precompile -o %t/a.pcm \
6+
// RUN: -DTEST_INLINE
7+
// RUN: %clang -std=c++20 %t/test.cc -fprebuilt-module-path=%t -fsyntax-only -Xclang -verify \
8+
// RUN: -DTEST_INLINE
9+
//
10+
// RUN: %clang -std=c++20 %t/a.cppm --precompile -o %t/a.pcm
11+
// RUN: %clang -std=c++20 %t/test.cc -fprebuilt-module-path=%t -fsyntax-only -Xclang -verify
12+
13+
//--- a.h
14+
#ifdef TEST_INLINE
15+
#define INLINE inline
16+
#else
17+
#define INLINE
18+
#endif
19+
static INLINE void func(long) {}
20+
template <typename T = long> void a() { func(T{}); }
21+
22+
//--- a.cppm
23+
module;
24+
#include "a.h"
25+
export module a;
26+
export using ::a;
27+
28+
//--- test.cc
29+
import a;
30+
auto m = (a(), 0);
31+
32+
#ifdef TEST_INLINE
33+
// expected-no-diagnostics
34+
#else
35+
// [email protected]:7 {{no matching function for call to 'func'}}
36+
// [email protected]:2 {{in instantiation of function template specialization 'a<long>' requested here}}
37+
#endif
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// RUN: rm -rf %t
2+
// RUN: mkdir -p %t
3+
// RUN: split-file %s %t
4+
//
5+
// RUN: %clang -std=c++20 %t/a.cppm --precompile -o %t/a.pcm
6+
// RUN: %clang -std=c++20 %t/test.cc -fprebuilt-module-path=%t -fsyntax-only -Xclang -verify
7+
8+
//--- a.h
9+
template <typename G> static inline void func() {}
10+
template <typename T = long> void a() { func<T>(); }
11+
12+
//--- a.cppm
13+
module;
14+
#include "a.h"
15+
export module a;
16+
export using ::a;
17+
18+
//--- test.cc
19+
import a;
20+
auto m = (a(), 0);
21+
22+
// expected-no-diagnostics
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// RUN: rm -rf %t
2+
// RUN: mkdir -p %t
3+
// RUN: split-file %s %t
4+
//
5+
// RUN: %clang -std=c++20 %t/a.cppm --precompile -o %t/a.pcm
6+
// RUN: %clang -std=c++20 %t/test.cc -fprebuilt-module-path=%t -fsyntax-only -Xclang -verify
7+
8+
//--- a.h
9+
namespace ns {
10+
template <typename G> static void func() {}
11+
template <typename T = long> void a() { func<T>(); }
12+
}
13+
14+
//--- a.cppm
15+
module;
16+
#include "a.h"
17+
export module a;
18+
export using ns::a;
19+
20+
//--- test.cc
21+
import a;
22+
auto m = (a(), 0);
23+
24+
// expected-no-diagnostics
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// RUN: rm -rf %t
2+
// RUN: mkdir -p %t
3+
// RUN: split-file %s %t
4+
//
5+
// RUN: %clang -std=c++20 %t/a.cppm --precompile -o %t/a.pcm \
6+
// RUN: -DTEST_INLINE
7+
// RUN: %clang -std=c++20 %t/test.cc -fprebuilt-module-path=%t -fsyntax-only -Xclang -verify \
8+
// RUN: -DTEST_INLINE
9+
//
10+
// RUN: %clang -std=c++20 %t/a.cppm --precompile -o %t/a.pcm
11+
// RUN: %clang -std=c++20 %t/test.cc -fprebuilt-module-path=%t -fsyntax-only -Xclang -verify
12+
13+
//--- a.h
14+
#ifdef TEST_INLINE
15+
#define INLINE inline
16+
#else
17+
#define INLINE
18+
#endif
19+
namespace ns {
20+
template <typename G> static void func() {}
21+
template <> INLINE void func<long>() {}
22+
template <typename T = long> void a() { func<T>(); }
23+
}
24+
25+
//--- a.cppm
26+
module;
27+
#include "a.h"
28+
export module a;
29+
export using ns::a;
30+
31+
//--- test.cc
32+
import a;
33+
auto m = (a(), 0);
34+
35+
#ifdef TEST_INLINE
36+
// expected-no-diagnostics
37+
#else
38+
// [email protected]:9 {{no matching function for call to 'func'}}
39+
// [email protected]:2 {{in instantiation of function template specialization 'ns::a<long>' requested here}}
40+
#endif
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// RUN: rm -rf %t
2+
// RUN: mkdir -p %t
3+
// RUN: split-file %s %t
4+
//
5+
// RUN: %clang -std=c++20 %t/a.cppm --precompile -o %t/a.pcm
6+
// RUN: %clang -std=c++20 %t/test.cc -fprebuilt-module-path=%t -fsyntax-only -Xclang -verify
7+
8+
//--- a.h
9+
namespace ns {
10+
namespace {
11+
template <typename G> void func() {}
12+
}
13+
template <typename T = long> void a() { func<T>(); }
14+
}
15+
16+
//--- a.cppm
17+
module;
18+
#include "a.h"
19+
export module a;
20+
export using ns::a;
21+
22+
//--- test.cc
23+
import a;
24+
auto m = (a(), 0);
25+
26+
// expected-no-diagnostics

0 commit comments

Comments
 (0)