diff --git a/clang/include/clang/Sema/Overload.h b/clang/include/clang/Sema/Overload.h --- a/clang/include/clang/Sema/Overload.h +++ b/clang/include/clang/Sema/Overload.h @@ -795,6 +795,10 @@ /// This candidate was not viable because its associated constraints were /// not satisfied. ovl_fail_constraints_not_satisfied, + + /// This candidate was not viable because it has internal linkage and is + /// from a different module unit than the use. + ovl_fail_module_mismatched, }; /// A list of implicit conversion sequences for the arguments of an diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -3838,6 +3838,12 @@ // associated classes are visible within their respective // namespaces even if they are not visible during an ordinary // lookup (11.4). + // + // C++20 [basic.lookup.argdep] p4.3 + // -- are exported, are attached to a named module M, do not appear + // in the translation unit containing the point of the lookup, and + // have the same innermost enclosing non-inline namespace scope as + // a declaration of an associated entity attached to M. DeclContext::lookup_result R = NS->lookup(Name); for (auto *D : R) { auto *Underlying = D; @@ -3858,6 +3864,36 @@ if (isVisible(D)) { Visible = true; break; + } else if (getLangOpts().CPlusPlusModules && + D->isInExportDeclContext()) { + // C++20 [basic.lookup.argdep] p4.3 .. are exported ... + Module *FM = D->getOwningModule(); + // exports are only valid in module purview and outside of any + // PMF (although a PMF should not even be present in a module + // with an import). + assert(FM && FM->isModulePurview() && !FM->isPrivateModule() && + "bad export context"); + // .. are attached to a named module M, do not appear in the + // translation unit containing the point of the lookup.. + if (!isModuleUnitOfCurrentTU(FM) && + llvm::any_of(AssociatedClasses, [&](auto *E) { + // ... and have the same innermost enclosing non-inline + // namespace scope as a declaration of an associated entity + // attached to M + if (!E->hasOwningModule() || + E->getOwningModule()->getTopLevelModuleName() != + FM->getTopLevelModuleName()) + return false; + // TODO: maybe this could be cached when generating the + // associated namespaces / entities. + DeclContext *Ctx = E->getDeclContext(); + while (!Ctx->isFileContext() || Ctx->isInlineNamespace()) + Ctx = Ctx->getParent(); + return Ctx == NS; + })) { + Visible = true; + break; + } } } else if (D->getFriendObjectKind()) { auto *RD = cast(D->getLexicalDeclContext()); diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -6400,6 +6400,17 @@ return; } + // Functions with internal linkage are only viable in the same module unit. + if (auto *MF = Function->getOwningModule()) { + if (getLangOpts().CPlusPlusModules && !MF->isModuleMapModule() && + Function->getFormalLinkage() <= Linkage::InternalLinkage && + !isModuleUnitOfCurrentTU(MF)) { + Candidate.Viable = false; + Candidate.FailureKind = ovl_fail_module_mismatched; + return; + } + } + if (Function->isMultiVersion() && Function->hasAttr() && !Function->getAttr()->isDefaultVersion()) { Candidate.Viable = false; diff --git a/clang/test/CXX/basic/basic.lookup/basic.lookup.argdep/p5-ex2.cpp b/clang/test/CXX/basic/basic.lookup/basic.lookup.argdep/p5-ex2.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CXX/basic/basic.lookup/basic.lookup.argdep/p5-ex2.cpp @@ -0,0 +1,68 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: split-file %s %t +// RUN: cd %t +// +// RUN: %clang_cc1 -std=c++20 M.cpp -emit-module-interface -o M.pcm +// RUN: %clang_cc1 -std=c++20 N.cpp -emit-module-interface -o N.pcm \ +// RUN: -fmodule-file=M.pcm +// RUN: %clang_cc1 -std=c++20 Q.cpp -emit-module-interface -o Q.pcm +// RUN: %clang_cc1 -std=c++20 Q-impl.cpp -fsyntax-only -fmodule-file=Q.pcm \ +// RUN: -fmodule-file=N.pcm -verify + +//--- M.cpp +export module M; +namespace R { +export struct X {}; +export void f(X); +} // namespace R +namespace S { +export void f(R::X, R::X); +} + +//--- N.cpp +export module N; +import M; +export R::X make(); +namespace R { +static int g(X); +} +export template +void apply(T t, U u) { + f(t, u); + g(t); +} + +//--- Q.cpp +export module Q; + +//--- Q-impl.cpp +module Q; +import N; + +namespace S { +struct Z { + template operator T(); +}; +} // namespace S +void test() { + // OK, decltype(x) is R::X in module M + auto x = make(); + + // error: R and R::f are not visible here + R::f(x); // expected-error {{declaration of 'R' must be imported from module 'N' before it is required}} + // expected-note@N.cpp:4 {{declaration here is not visible}} + // expected-error@-2 {{no type named 'f' in namespace 'R'}} + + f(x); // Found by [basic.lookup.argdep] / p4.3 + + // error: S::f in module M not considered even though S is an associated + // namespace, since the entity Z is in a different module from f. + f(x, S::Z()); // expected-error {{no matching function for call to 'f'}} + // expected-note@M.cpp:4 {{candidate function not viable: requires 1 argument, but 2 were provided}} + + // error: S::f is visible in instantiation context, but R::g has internal + // linkage and cannot be used outside N.cpp + apply(x, S::Z()); // expected-error@N.cpp:10 {{no matching function for call to 'g'}} + // expected-note@-1 {{in instantiation of function template specialization 'apply' requested here}} +}