diff options
author | Jan Svoboda <jan_svoboda@apple.com> | 2023-01-19 17:02:42 -0800 |
---|---|---|
committer | Jan Svoboda <jan_svoboda@apple.com> | 2023-01-20 13:37:36 -0800 |
commit | c3efd52770ca964ba46287298c1d2f7697fd446c (patch) | |
tree | ba336fee3953bfb00a7c8bbacb6764b85a415770 | |
parent | 743fbcb79d9af759377df5f5929ffdd38ff52b09 (diff) | |
download | llvm-c3efd52770ca964ba46287298c1d2f7697fd446c.zip llvm-c3efd52770ca964ba46287298c1d2f7697fd446c.tar.gz llvm-c3efd52770ca964ba46287298c1d2f7697fd446c.tar.bz2 |
[clang][modules] Disallow importing private framework in the implementation
Whenever we are compiling implementation of a framework (with the `-fmodule-name=FW` option), we never translate `#import <FW/Header.h>` to an import, regardless of whether "Header.h" belongs to "FW" or "FW_Private". For the same reasons, we also disallow `@import FW`. However, we still allow `@import FW_Private`. This patch disallows that a well, to be consistent with the rest of the rules.
Reviewed By: benlangmuir
Differential Revision: https://reviews.llvm.org/D142167
-rw-r--r-- | clang/include/clang/Basic/Module.h | 3 | ||||
-rw-r--r-- | clang/lib/Basic/Module.cpp | 20 | ||||
-rw-r--r-- | clang/lib/Lex/PPDirectives.cpp | 32 | ||||
-rw-r--r-- | clang/lib/Sema/SemaModule.cpp | 2 | ||||
-rw-r--r-- | clang/test/Modules/implementation-of-module-private.m | 13 |
5 files changed, 42 insertions, 28 deletions
diff --git a/clang/include/clang/Basic/Module.h b/clang/include/clang/Basic/Module.h index fc0ca16..c042cf1 100644 --- a/clang/include/clang/Basic/Module.h +++ b/clang/include/clang/Basic/Module.h @@ -469,6 +469,9 @@ public: bool isUnimportable(const LangOptions &LangOpts, const TargetInfo &Target, Requirement &Req, Module *&ShadowingModule) const; + /// Determine whether this module can be built in this compilation. + bool isForBuilding(const LangOptions &LangOpts) const; + /// Determine whether this module is available for use within the /// current translation unit. bool isAvailable() const { return IsAvailable; } diff --git a/clang/lib/Basic/Module.cpp b/clang/lib/Basic/Module.cpp index aa82a99..9c4c834 100644 --- a/clang/lib/Basic/Module.cpp +++ b/clang/lib/Basic/Module.cpp @@ -148,6 +148,26 @@ bool Module::isUnimportable(const LangOptions &LangOpts, llvm_unreachable("could not find a reason why module is unimportable"); } +// The -fmodule-name option tells the compiler to textually include headers in +// the specified module, meaning Clang won't build the specified module. This +// is useful in a number of situations, for instance, when building a library +// that vends a module map, one might want to avoid hitting intermediate build +// products containing the module map or avoid finding the system installed +// modulemap for that library. +bool Module::isForBuilding(const LangOptions &LangOpts) const { + StringRef TopLevelName = getTopLevelModuleName(); + StringRef CurrentModule = LangOpts.CurrentModule; + + // When building framework Foo, we want to make sure that Foo *and* + // Foo_Private are textually included and no modules are built for both. + if (getTopLevelModule()->IsFramework && + CurrentModule == LangOpts.ModuleName && + !CurrentModule.endswith("_Private") && TopLevelName.endswith("_Private")) + TopLevelName = TopLevelName.drop_back(8); + + return TopLevelName == CurrentModule; +} + bool Module::isAvailable(const LangOptions &LangOpts, const TargetInfo &Target, Requirement &Req, UnresolvedHeaderDirective &MissingHeader, diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp index 41b38b6..f08d5fe 100644 --- a/clang/lib/Lex/PPDirectives.cpp +++ b/clang/lib/Lex/PPDirectives.cpp @@ -109,25 +109,6 @@ enum PPElifDiag { PED_Elifndef }; -// The -fmodule-name option tells the compiler to textually include headers in -// the specified module, meaning clang won't build the specified module. This is -// useful in a number of situations, for instance, when building a library that -// vends a module map, one might want to avoid hitting intermediate build -// products containimg the module map or avoid finding the system installed -// modulemap for that library. -static bool isForModuleBuilding(Module *M, StringRef CurrentModule, - StringRef ModuleName) { - StringRef TopLevelName = M->getTopLevelModuleName(); - - // When building framework Foo, we wanna make sure that Foo *and* Foo_Private - // are textually included and no modules are built for both. - if (M->getTopLevelModule()->IsFramework && CurrentModule == ModuleName && - !CurrentModule.endswith("_Private") && TopLevelName.endswith("_Private")) - TopLevelName = TopLevelName.drop_back(8); - - return TopLevelName == CurrentModule; -} - static MacroDiag shouldWarnOnMacroDef(Preprocessor &PP, IdentifierInfo *II) { const LangOptions &Lang = PP.getLangOpts(); if (isReservedInAllContexts(II->isReserved(Lang))) { @@ -2219,14 +2200,13 @@ Preprocessor::ImportAction Preprocessor::HandleHeaderIncludeOrImport( alreadyIncluded(*File)) Action = IncludeLimitReached; - bool MaybeTranslateInclude = Action == Enter && File && SuggestedModule && - !isForModuleBuilding(SuggestedModule.getModule(), - getLangOpts().CurrentModule, - getLangOpts().ModuleName); - // FIXME: We do not have a good way to disambiguate C++ clang modules from // C++ standard modules (other than use/non-use of Header Units). Module *SM = SuggestedModule.getModule(); + + bool MaybeTranslateInclude = + Action == Enter && File && SM && !SM->isForBuilding(getLangOpts()); + // Maybe a usable Header Unit bool UsableHeaderUnit = false; if (getLangOpts().CPlusPlusModules && SM && SM->isHeaderUnit()) { @@ -2556,9 +2536,7 @@ Preprocessor::ImportAction Preprocessor::HandleHeaderIncludeOrImport( // that behaves the same as the header would behave in a compilation using // that PCH, which means we should enter the submodule. We need to teach // the AST serialization layer to deal with the resulting AST. - if (getLangOpts().CompilingPCH && - isForModuleBuilding(SM, getLangOpts().CurrentModule, - getLangOpts().ModuleName)) + if (getLangOpts().CompilingPCH && SM->isForBuilding(getLangOpts())) return {ImportAction::None}; assert(!CurLexerSubmodule && "should not have marked this as a module yet"); diff --git a/clang/lib/Sema/SemaModule.cpp b/clang/lib/Sema/SemaModule.cpp index 8fa31ea..823a4e9 100644 --- a/clang/lib/Sema/SemaModule.cpp +++ b/clang/lib/Sema/SemaModule.cpp @@ -541,7 +541,7 @@ DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc, // of the same top-level module. Until we do, make it an error rather than // silently ignoring the import. // FIXME: Should we warn on a redundant import of the current module? - if (Mod->getTopLevelModuleName() == getLangOpts().CurrentModule && + if (Mod->isForBuilding(getLangOpts()) && (getLangOpts().isCompilingModule() || !getLangOpts().ModulesTS)) { Diag(ImportLoc, getLangOpts().isCompilingModule() ? diag::err_module_self_import diff --git a/clang/test/Modules/implementation-of-module-private.m b/clang/test/Modules/implementation-of-module-private.m new file mode 100644 index 0000000..f6ca2a8 --- /dev/null +++ b/clang/test/Modules/implementation-of-module-private.m @@ -0,0 +1,13 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t + +//--- frameworks/FW.framework/Modules/module.modulemap +framework module FW {} +//--- frameworks/FW.framework/Modules/module.private.modulemap +framework module FW_Private {} + +//--- tu.m +@import FW_Private; // expected-error{{@import of module 'FW_Private' in implementation of 'FW'; use #import}} + +// RUN: %clang_cc1 -fmodules -fmodules-cache-path=%t/cache -fimplicit-module-maps \ +// RUN: -fmodule-name=FW -F %t/frameworks %t/tu.m -verify |