diff --git a/clang/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td --- a/clang/include/clang/Basic/DiagnosticLexKinds.td +++ b/clang/include/clang/Basic/DiagnosticLexKinds.td @@ -793,6 +793,12 @@ "module '%0' conflicts with already-imported module '%1': %2">, InGroup; +def err_module_internal_header_use : Error< + "header '%0' is a libc++ detail header and can't be included by users">; +def err_module_internal_header_use_with_user_facing : Error< + "header '%0' is a libc++ detail header and can't be included by users; " + "include '<%1>' instead, which is user-facing">; + // C++20 modules def err_header_import_semi_in_macro : Error< "semicolon terminating header import declaration cannot be produced " diff --git a/clang/lib/Lex/ModuleMap.cpp b/clang/lib/Lex/ModuleMap.cpp --- a/clang/lib/Lex/ModuleMap.cpp +++ b/clang/lib/Lex/ModuleMap.cpp @@ -470,6 +470,26 @@ return M ? M->getTopLevelModule() : nullptr; } +static bool implementationHeaderUsedExternally(const Module *RequestingModule, + StringRef Filename) { + return Filename.startswith("__libcxx") && + (RequestingModule == nullptr || + RequestingModule->getTopLevelModuleName() != "std"); +} + +static std::string FindAppropriateStdlibHeader(const Module *StdlibModule) { + const Module *OriginalModule = StdlibModule; + while (StdlibModule->Parent && StdlibModule->Parent->Name != "std") { + StdlibModule = StdlibModule->Parent; + } + + if (StdlibModule == OriginalModule || StdlibModule->Headers[0].empty()) { + return ""; + } + + return StdlibModule->Headers[0][0].NameAsWritten; +} + void ModuleMap::diagnoseHeaderInclusion(Module *RequestingModule, bool RequestingModuleIsModuleInterface, SourceLocation FilenameLoc, @@ -488,6 +508,7 @@ bool Excluded = false; Module *Private = nullptr; Module *NotUsed = nullptr; + Module *StdlibImplModule = nullptr; HeadersMap::iterator Known = findKnownHeader(File); if (Known != Headers.end()) { @@ -498,6 +519,14 @@ continue; } + // Headers with "__" in their name are reserved for implementation use + // only. + if (LangOpts.CPlusPlus && + implementationHeaderUsedExternally(RequestingModule, Filename)) { + StdlibImplModule = Header.getModule(); + continue; + } + // If uses need to be specified explicitly, we are only allowed to return // modules that are explicitly used by the requesting module. if (RequestingModule && LangOpts.ModulesDeclUse && @@ -520,6 +549,18 @@ return; } + // Header is owned by the standard library + if (StdlibImplModule) { + const std::string AppropriateHeader = + FindAppropriateStdlibHeader(StdlibImplModule); + Diags.Report(FilenameLoc, + AppropriateHeader.empty() + ? diag::err_module_internal_header_use + : diag::err_module_internal_header_use_with_user_facing) + << Filename << AppropriateHeader; + return; + } + // We have found a module, but we don't use it. if (NotUsed) { Diags.Report(FilenameLoc, diag::err_undeclared_use_of_module) diff --git a/clang/test/Modules/Inputs/libcxx-reserved/__libcxx/horde_prime.h b/clang/test/Modules/Inputs/libcxx-reserved/__libcxx/horde_prime.h new file mode 100644 --- /dev/null +++ b/clang/test/Modules/Inputs/libcxx-reserved/__libcxx/horde_prime.h @@ -0,0 +1 @@ +// This file is intentionally empty diff --git a/clang/test/Modules/Inputs/libcxx-reserved/__libcxx/princesses/she-ra.h b/clang/test/Modules/Inputs/libcxx-reserved/__libcxx/princesses/she-ra.h new file mode 100644 --- /dev/null +++ b/clang/test/Modules/Inputs/libcxx-reserved/__libcxx/princesses/she-ra.h @@ -0,0 +1,6 @@ +#ifndef PRIVATE_HEADER_1 +#define PRIVATE_HEADER_1 + +using i32 = int; + +#endif // PRIVATE_HEADER_1 diff --git a/clang/test/Modules/Inputs/libcxx-reserved/module.modulemap b/clang/test/Modules/Inputs/libcxx-reserved/module.modulemap new file mode 100644 --- /dev/null +++ b/clang/test/Modules/Inputs/libcxx-reserved/module.modulemap @@ -0,0 +1,12 @@ +module std { + module princesses { + header "princesses.h" + export * + + module __princesses { + module she_ra { header "__libcxx/princesses/she-ra.h" } + } + } + + module __horde_prime { header "__libcxx/horde_prime.h" } +} diff --git a/clang/test/Modules/Inputs/libcxx-reserved/princesses.h b/clang/test/Modules/Inputs/libcxx-reserved/princesses.h new file mode 100644 --- /dev/null +++ b/clang/test/Modules/Inputs/libcxx-reserved/princesses.h @@ -0,0 +1,7 @@ +#ifndef PUBLIC_HEADER_1 +#define PUBLIC_HEADER_1 + +#include "__libcxx/horde_prime.h" +#include "__libcxx/princesses/she-ra.h" + +#endif // PUBLIC_HEADER_1 diff --git a/clang/test/Modules/libcxx-reserved.cpp b/clang/test/Modules/libcxx-reserved.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Modules/libcxx-reserved.cpp @@ -0,0 +1,14 @@ +// RUN: rm -rf %t +// RUN: %clang_cc1 -fmodules -fmodules-cache-path=%t \ +// RUN: -fmodule-map-file=%S/Inputs/libcxx-reserved/module.modulemap \ +// RUN: -fmodules-local-submodule-visibility -I%S/Inputs/libcxx-reserved -verify %s +// RUN: ls %t + +#include "princesses.h" +#include "__libcxx/princesses/she-ra.h" +// expected-error@-1{{header '__libcxx/princesses/she-ra.h' is a libc++ detail header and can't be included by users; include '' instead, which is user-facing}} +#include "__libcxx/horde_prime.h" +// expected-error@-1{{header '__libcxx/horde_prime.h' is a libc++ detail header and can't be included by users}} + +int main() +{}