[clang] e50ec3e - [Clang][Sema] Expose static inline functions from GMF (#104701)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Dec 30 17:53:33 PST 2024
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 at a.h:7 {{no matching function for call to 'func'}}
+// expected-note at 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 at a.h:9 {{no matching function for call to 'func'}}
+// expected-note at 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
More information about the cfe-commits
mailing list