https://github.com/combinatorial updated https://github.com/llvm/llvm-project/pull/210825
>From fb78a6b2326248aeeedff057e008cbec06b973e9 Mon Sep 17 00:00:00 2001 From: David Brittain <[email protected]> Date: Mon, 20 Jul 2026 21:56:47 +0000 Subject: [PATCH] [clang][Modules] Keep current-TU global-module-fragment internal-linkage functions viable in overload resolution An internal-linkage function (e.g. a `static` or anonymous-namespace helper) declared in the global module fragment of the current translation unit is usable within the module unit ([module.global.frag]/p1). However, Sema::AddOverloadCandidate removed such a function from the overload set whenever Function->isInAnotherModuleUnit() was true. That predicate misfires for a current-TU GMF declaration when the enclosing template is instantiated after the global module fragment is closed: pending instantiations run in ActOnEndOfTranslationUnitFragment, and isInAnotherModuleUnit() consults ASTContext's current named module, which is not in scope yet at that point. The candidate was then dropped with the hidden ovl_fail_module_mismatched failure kind -- yielding a spurious "no matching function" error with no candidate notes. The existing carve-out only exempted inline / implicitly-instantiated GMF functions (added in #104701), so a plain `static` helper reached through such a template still failed. Recognise a global-module-fragment function as belonging to the current unit by comparing its top-level module against Sema::getCurrentModule() (the module scope, which is still correct while the GMF is being finalised), and keep it in the overload set. Uses of a `static` function genuinely imported from another module unit remain subject to the internal-linkage rule. Fixes #210822. --- clang/docs/ReleaseNotes.md | 7 ++++ clang/lib/Sema/SemaOverload.cpp | 11 +++++- .../gmf-internal-linkage-in-template.cppm | 37 +++++++++++++++++++ 3 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 clang/test/Modules/gmf-internal-linkage-in-template.cppm diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index 5a07e77076d17..dbbf858ad68be 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -323,6 +323,13 @@ latest release, please see the [Clang Web Site](https://clang.llvm.org) or the - Fixed a crash when a using-declaration naming an unresolvable member of a dependent base was shadowed by an invalid using-declaration. (#GH209427) +- Fixed a regression where an internal-linkage function (e.g. a `static` or + anonymous-namespace helper) declared in the global module fragment of the + current translation unit was removed from the overload set when the calling + template was instantiated after the global module fragment was closed, + producing a spurious "no matching function" error with no candidate notes. + (#GH210822) + #### Bug Fixes to AST Handling - Fixed a non-deterministic ordering of unused local typedefs that made diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index dc912e8e95fc4..d66dea0d918fa 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -7388,7 +7388,16 @@ void Sema::AddOverloadCandidate( Function->isFromGlobalModule() && (IsImplicitlyInstantiated || Function->isInlined()); - if (ND->getFormalLinkage() == Linkage::Internal && !IsInlineFunctionInGMF) { + // Don't exclude internal-linkage entities from the current TU's global + // module fragment. + const Module *CurrentModule = getCurrentModule(); + const bool IsCurrentUnitGMFDecl = + Function->isFromGlobalModule() && CurrentModule && + Function->getOwningModule()->getTopLevelModule() == + CurrentModule->getTopLevelModule(); + + if (ND->getFormalLinkage() == Linkage::Internal && !IsInlineFunctionInGMF && + !IsCurrentUnitGMFDecl) { Candidate.Viable = false; Candidate.FailureKind = ovl_fail_module_mismatched; return; diff --git a/clang/test/Modules/gmf-internal-linkage-in-template.cppm b/clang/test/Modules/gmf-internal-linkage-in-template.cppm new file mode 100644 index 0000000000000..faa41fe2ba041 --- /dev/null +++ b/clang/test/Modules/gmf-internal-linkage-in-template.cppm @@ -0,0 +1,37 @@ +// Verify that an internal-linkage function declared in the global module +// fragment of the current translation unit remains usable from within the +// module unit when it is only reached through a template instantiation that is +// performed after the global module fragment is closed (i.e. the instantiation +// is triggered by another entity in the global module fragment). +// +// This is a regression test: such a plain 'static' (non-inline) helper used to +// be wrongly removed from the overload set, producing a bogus "no matching +// function" error with no candidate notes. +// +// RUN: %clang_cc1 -std=c++20 %s -emit-module-interface -o %t.pcm -verify +// RUN: %clang_cc1 -std=c++23 %s -emit-module-interface -o %t.pcm -verify + +// expected-no-diagnostics + +module; +static void slow(unsigned long *o) { *o = 0; } +static void slow(unsigned int *o) { *o = 0; } + +template <typename T> void parse(T *o) { slow(o); } + +// These inline functions are in the global module fragment; the template +// specializations they use are instantiated when the fragment is closed. +inline unsigned long read64() { + unsigned long t; + parse(&t); + return t; +} +inline unsigned int read32() { + unsigned int t; + parse(&t); + return t; +} + +export module a; + +export inline unsigned long use() { return read64() + read32(); } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
