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 @@ -3827,6 +3827,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; @@ -3847,6 +3853,30 @@ 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(); + // .. are attached to a named module M, do not appear in the + // translation unit containing the point of the lookup.. + if (FM->isModulePurview() && + (ModuleScopes.empty() || FM != ModuleScopes.back().Module)) { + for (auto *E : AssociatedClasses) { + // 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() != FM) + continue; + // TODO: maybe this could be cached when generating the + // associated namespaces / entities. + DeclContext *Ctx = E->getDeclContext(); + while (!Ctx->isFileContext() || Ctx->isInlineNamespace()) + Ctx = Ctx->getParent(); + if (Ctx == NS) + Visible = true; + } + if (Visible) + 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 (Function->getFormalLinkage() <= Linkage::InternalLinkage && + getLangOpts().CPlusPlusModules && + MF->getTopLevelModule() != getCurrentModule()->getTopLevelModule()) { + 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.link/p10-ex2.cpp b/clang/test/CXX/basic/basic.link/p10-ex2.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CXX/basic/basic.link/p10-ex2.cpp @@ -0,0 +1,35 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: split-file %s %t +// RUN: cd %t +// +// RUN: %clang_cc1 -std=c++20 M.cpp -fsyntax-only -DTEST_INTERFACE -verify +// RUN: %clang_cc1 -std=c++20 M.cpp -emit-module-interface -o M.pcm +// RUN: %clang_cc1 -std=c++20 useM.cpp -fsyntax-only -fmodule-file=M.pcm -verify + +//--- decls.h +int f(); // #1, attached to the global module +int g(); // #2, attached to the global module + +//--- M.cpp +module; +#include "decls.h" +export module M; +export using ::f; // OK, does not declare an entity, exports #1 +#if TEST_INTERFACE +// error: matches #2, but attached to M +int g(); // expected-error {{declaration of 'g' in module M follows declaration in the global module}} +// expected-note@decls.h:2 {{previous declaration is here}} +#endif +export int h(); // #3 +export int k(); // #4 + +//--- useM.cpp +import M; +// error: matches #3 +static int h(); // expected-error {{static declaration of 'h' follows non-static declaration}} +// expected-note@M.cpp:10 {{previous declaration is here}} + +// error: matches #4 +int k(); // expected-error {{declaration of 'k' in the global module follows declaration in module M}} +// expected-note@M.cpp:11 {{previous declaration is here}} 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}} +}