From 97eec24b0bc245fc96ac9615899c3d15a6bc8b3e Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Thu, 15 Sep 2011 22:00:41 +0000 Subject: [PATCH] Add an experimental flag -fauto-module-import that automatically turns #include or #import direcctives of framework headers into module imports of the corresponding framework module. llvm-svn: 139860 --- clang/include/clang/Driver/CC1Options.td | 5 ++- clang/include/clang/Driver/Options.td | 3 ++ .../clang/Frontend/PreprocessorOptions.h | 5 +++ clang/include/clang/Lex/DirectoryLookup.h | 14 ++++++- clang/include/clang/Lex/HeaderSearch.h | 18 ++++++-- clang/include/clang/Lex/Preprocessor.h | 11 ++++- clang/lib/Driver/Tools.cpp | 2 + clang/lib/Frontend/CompilerInstance.cpp | 5 ++- clang/lib/Frontend/CompilerInvocation.cpp | 1 + clang/lib/Frontend/InitPreprocessor.cpp | 6 ++- clang/lib/Lex/HeaderSearch.cpp | 42 +++++++++++++++---- clang/lib/Lex/PPDirectives.cpp | 20 +++++++-- clang/lib/Lex/PPMacroExpansion.cpp | 2 +- clang/lib/Lex/Pragma.cpp | 3 +- clang/lib/Lex/Preprocessor.cpp | 1 + .../Headers/DependsOnModule.h | 3 ++ .../Inputs/Module.framework/Headers/Module.h | 1 + clang/test/Modules/auto-module-import.c | 13 ++++++ 18 files changed, 130 insertions(+), 25 deletions(-) create mode 100644 clang/test/Modules/Inputs/DependsOnModule.framework/Headers/DependsOnModule.h create mode 100644 clang/test/Modules/auto-module-import.c diff --git a/clang/include/clang/Driver/CC1Options.td b/clang/include/clang/Driver/CC1Options.td index 0609e75a25ab..356a76e1e778 100644 --- a/clang/include/clang/Driver/CC1Options.td +++ b/clang/include/clang/Driver/CC1Options.td @@ -607,7 +607,10 @@ def fmodule_cache_path : Separate<"-fmodule-cache-path">, HelpText<"Specify the module cache path">; def fdisable_module_hash : Flag<"-fdisable-module-hash">, HelpText<"Disable the module hash">; - +def fauto_module_import : Flag<"-fauto-module-import">, + HelpText<"Automatically translate #include/#import into module imports " + "when possible">; + def F : JoinedOrSeparate<"-F">, MetaVarName<"">, HelpText<"Add directory to framework include search path">; def I : JoinedOrSeparate<"-I">, MetaVarName<"">, diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 55bf2e9db387..d9f34f55acae 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -344,6 +344,9 @@ def fmsc_version : Joined<"-fmsc-version=">, Group; def fdelayed_template_parsing : Flag<"-fdelayed-template-parsing">, Group; def fmodule_cache_path : Separate<"-fmodule-cache-path">, Group, Flags<[NoForward]>; +def fauto_module_import : Flag <"-fauto-module-import">, Group, + Flags<[NoForward]>; + def fmudflapth : Flag<"-fmudflapth">, Group; def fmudflap : Flag<"-fmudflap">, Group; def fnested_functions : Flag<"-fnested-functions">, Group; diff --git a/clang/include/clang/Frontend/PreprocessorOptions.h b/clang/include/clang/Frontend/PreprocessorOptions.h index 0259cfd26038..30a34060b25b 100644 --- a/clang/include/clang/Frontend/PreprocessorOptions.h +++ b/clang/include/clang/Frontend/PreprocessorOptions.h @@ -50,6 +50,10 @@ public: /// record of all macro definitions and /// expansions. + /// \brief Whether we should automatically translate #include or #import + /// operations into module imports when possible. + unsigned AutoModuleImport : 1; + /// \brief Whether the detailed preprocessing record includes nested macro /// expansions. unsigned DetailedRecordIncludesNestedMacroExpansions : 1; @@ -162,6 +166,7 @@ public: public: PreprocessorOptions() : UsePredefines(true), DetailedRecord(false), + AutoModuleImport(false), DetailedRecordIncludesNestedMacroExpansions(true), DisablePCHValidation(false), DisableStatCache(false), DumpDeserializedPCHDecls(false), diff --git a/clang/include/clang/Lex/DirectoryLookup.h b/clang/include/clang/Lex/DirectoryLookup.h index ae5eabc690b5..f7da61b0ed71 100644 --- a/clang/include/clang/Lex/DirectoryLookup.h +++ b/clang/include/clang/Lex/DirectoryLookup.h @@ -140,15 +140,25 @@ public: /// \param RelativePath If not NULL, will be set to the path relative to /// SearchPath at which the file was found. This only differs from the /// Filename for framework includes. + /// + /// \param BuildingModule The name of the module we're currently building. + /// + /// \param SuggestedModule If non-null, and the file found is semantically + /// part of a known module, this will be set to the name of the module that + /// could be imported instead of preprocessing/parsing the file found. const FileEntry *LookupFile(StringRef Filename, HeaderSearch &HS, SmallVectorImpl *SearchPath, - SmallVectorImpl *RelativePath) const; + SmallVectorImpl *RelativePath, + StringRef BuildingModule, + StringRef *SuggestedModule) const; private: const FileEntry *DoFrameworkLookup( StringRef Filename, HeaderSearch &HS, SmallVectorImpl *SearchPath, - SmallVectorImpl *RelativePath) const; + SmallVectorImpl *RelativePath, + StringRef BuildingModule, + StringRef *SuggestedModule) const; }; diff --git a/clang/include/clang/Lex/HeaderSearch.h b/clang/include/clang/Lex/HeaderSearch.h index 86aa7c974a6c..84d59f793bbb 100644 --- a/clang/include/clang/Lex/HeaderSearch.h +++ b/clang/include/clang/Lex/HeaderSearch.h @@ -132,6 +132,9 @@ class HeaderSearch { /// \brief The path to the module cache. std::string ModuleCachePath; + /// \brief The name of the module we're building. + std::string BuildingModule; + /// FileInfo - This contains all of the preprocessor-specific data about files /// that are included. The vector is indexed by the FileEntry's UID. /// @@ -196,9 +199,11 @@ public: //LookupFileCache.clear(); } - /// \brief Set the path to the module cache. - void setModuleCachePath(StringRef Path) { - ModuleCachePath = Path; + /// \brief Set the path to the module cache and the name of the module + /// we're building + void configureModules(StringRef CachePath, StringRef BuildingModule) { + ModuleCachePath = CachePath; + this->BuildingModule = BuildingModule; } /// ClearFileInfo - Forget everything we know about headers so far. @@ -240,12 +245,17 @@ public: /// \param RelativePath If non-null, will be set to the path relative to /// SearchPath at which the file was found. This only differs from the /// Filename for framework includes. + /// + /// \param SuggestedModule If non-null, and the file found is semantically + /// part of a known module, this will be set to the name of the module that + /// could be imported instead of preprocessing/parsing the file found. const FileEntry *LookupFile(StringRef Filename, bool isAngled, const DirectoryLookup *FromDir, const DirectoryLookup *&CurDir, const FileEntry *CurFileEnt, SmallVectorImpl *SearchPath, - SmallVectorImpl *RelativePath); + SmallVectorImpl *RelativePath, + StringRef *SuggestedModule); /// LookupSubframeworkHeader - Look up a subframework for the specified /// #include file. For example, if #include'ing from diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h index c404149f1892..fc0e98b8b815 100644 --- a/clang/include/clang/Lex/Preprocessor.h +++ b/clang/include/clang/Lex/Preprocessor.h @@ -106,7 +106,8 @@ class Preprocessor : public llvm::RefCountedBase { bool KeepComments : 1; bool KeepMacroComments : 1; bool SuppressIncludeNotFoundError : 1; - + bool AutoModuleImport : 1; + // State that changes while the preprocessor runs: bool InMacroArgs : 1; // True if parsing fn macro invocation args. @@ -383,6 +384,11 @@ public: return SuppressIncludeNotFoundError; } + /// \brief Specify whether automatic module imports are enabled. + void setAutoModuleImport(bool AutoModuleImport = true) { + this->AutoModuleImport = AutoModuleImport; + } + /// isCurrentLexer - Return true if we are lexing directly from the specified /// lexer. bool isCurrentLexer(const PreprocessorLexer *L) const { @@ -973,7 +979,8 @@ public: bool isAngled, const DirectoryLookup *FromDir, const DirectoryLookup *&CurDir, SmallVectorImpl *SearchPath, - SmallVectorImpl *RelativePath); + SmallVectorImpl *RelativePath, + StringRef *SuggestedModule); /// GetCurLookup - The DirectoryLookup structure used to find the current /// FileEntry, if CurLexer is non-null and if applicable. This allows us to diff --git a/clang/lib/Driver/Tools.cpp b/clang/lib/Driver/Tools.cpp index 28a14a297030..0c97cf33f411 100644 --- a/clang/lib/Driver/Tools.cpp +++ b/clang/lib/Driver/Tools.cpp @@ -390,6 +390,8 @@ void Clang::AddPreprocessingOptions(const Driver &D, CmdArgs.push_back("-fmodule-cache-path"); CmdArgs.push_back(Args.MakeArgString(DefaultModuleCache)); } + + Args.AddAllArgs(CmdArgs, options::OPT_fauto_module_import); } /// getARMTargetCPU - Get the (LLVM) name of the ARM cpu we are targeting. diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp index c56cc3d9db46..92798c8c292d 100644 --- a/clang/lib/Frontend/CompilerInstance.cpp +++ b/clang/lib/Frontend/CompilerInstance.cpp @@ -228,7 +228,10 @@ void CompilerInstance::createPreprocessor() { if (!getHeaderSearchOpts().DisableModuleHash) llvm::sys::path::append(SpecificModuleCache, getInvocation().getModuleHash()); - PP->getHeaderSearchInfo().setModuleCachePath(SpecificModuleCache); + PP->getHeaderSearchInfo().configureModules(SpecificModuleCache, + getPreprocessorOpts().ModuleBuildPath.empty() + ? std::string() + : getPreprocessorOpts().ModuleBuildPath.back()); // Handle generating dependencies, if requested. const DependencyOutputOptions &DepOpts = getDependencyOutputOpts(); diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 4953f1741c3d..582fceff6961 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -1753,6 +1753,7 @@ static void ParsePreprocessorArgs(PreprocessorOptions &Opts, ArgList &Args, Opts.TokenCache = Opts.ImplicitPTHInclude; Opts.UsePredefines = !Args.hasArg(OPT_undef); Opts.DetailedRecord = Args.hasArg(OPT_detailed_preprocessing_record); + Opts.AutoModuleImport = Args.hasArg(OPT_fauto_module_import); Opts.DisablePCHValidation = Args.hasArg(OPT_fno_validate_pch); Opts.DumpDeserializedPCHDecls = Args.hasArg(OPT_dump_deserialized_pch_decls); diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp index 474de77f267b..e70ecf9da8c2 100644 --- a/clang/lib/Frontend/InitPreprocessor.cpp +++ b/clang/lib/Frontend/InitPreprocessor.cpp @@ -710,6 +710,10 @@ void clang::InitializePreprocessor(Preprocessor &PP, InitializeFileRemapping(PP.getDiagnostics(), PP.getSourceManager(), PP.getFileManager(), InitOpts); + // Specify whether the preprocessor should replace #include/#import with + // module imports when plausible. + PP.setAutoModuleImport(InitOpts.AutoModuleImport); + // Emit line markers for various builtin sections of the file. We don't do // this in asm preprocessor mode, because "# 4" is not a line marker directive // in this mode. @@ -783,7 +787,7 @@ void clang::InitializePreprocessor(Preprocessor &PP, // Copy PredefinedBuffer into the Preprocessor. PP.setPredefines(Predefines.str()); - + // Initialize the header search object. ApplyHeaderSearchOptions(PP.getHeaderSearchInfo(), HSOpts, PP.getLangOptions(), diff --git a/clang/lib/Lex/HeaderSearch.cpp b/clang/lib/Lex/HeaderSearch.cpp index bc18d0dba743..8a35080fe04f 100644 --- a/clang/lib/Lex/HeaderSearch.cpp +++ b/clang/lib/Lex/HeaderSearch.cpp @@ -139,7 +139,8 @@ const FileEntry *HeaderSearch::lookupModule(StringRef ModuleName, // Look for the umbrella header in this directory. if (const FileEntry *HeaderFile - = SearchDirs[Idx].LookupFile(UmbrellaHeaderName, *this, 0, 0)) { + = SearchDirs[Idx].LookupFile(UmbrellaHeaderName, *this, 0, 0, + StringRef(), 0)) { *UmbrellaHeader = HeaderFile->getName(); return 0; } @@ -173,7 +174,9 @@ const FileEntry *DirectoryLookup::LookupFile( StringRef Filename, HeaderSearch &HS, SmallVectorImpl *SearchPath, - SmallVectorImpl *RelativePath) const { + SmallVectorImpl *RelativePath, + StringRef BuildingModule, + StringRef *SuggestedModule) const { llvm::SmallString<1024> TmpDir; if (isNormalDir()) { // Concatenate the requested file onto the directory. @@ -192,7 +195,8 @@ const FileEntry *DirectoryLookup::LookupFile( } if (isFramework()) - return DoFrameworkLookup(Filename, HS, SearchPath, RelativePath); + return DoFrameworkLookup(Filename, HS, SearchPath, RelativePath, + BuildingModule, SuggestedModule); assert(isHeaderMap() && "Unknown directory lookup"); const FileEntry * const Result = getHeaderMap()->LookupFile( @@ -218,7 +222,10 @@ const FileEntry *DirectoryLookup::DoFrameworkLookup( StringRef Filename, HeaderSearch &HS, SmallVectorImpl *SearchPath, - SmallVectorImpl *RelativePath) const { + SmallVectorImpl *RelativePath, + StringRef BuildingModule, + StringRef *SuggestedModule) const +{ FileManager &FileMgr = HS.getFileMgr(); // Framework names must have a '/' in the filename. @@ -280,9 +287,15 @@ const FileEntry *DirectoryLookup::DoFrameworkLookup( SearchPath->append(FrameworkName.begin(), FrameworkName.end()-1); } + /// Determine whether this is the module we're building or not. + bool AutomaticImport = SuggestedModule && + (BuildingModule != StringRef(Filename.begin(), SlashPos)); + FrameworkName.append(Filename.begin()+SlashPos+1, Filename.end()); if (const FileEntry *FE = FileMgr.getFile(FrameworkName.str(), - /*openFile=*/true)) { + /*openFile=*/!AutomaticImport)) { + if (AutomaticImport) + *SuggestedModule = StringRef(Filename.begin(), SlashPos); return FE; } @@ -294,7 +307,11 @@ const FileEntry *DirectoryLookup::DoFrameworkLookup( SearchPath->insert(SearchPath->begin()+OrigSize, Private, Private+strlen(Private)); - return FileMgr.getFile(FrameworkName.str(), /*openFile=*/true); + const FileEntry *FE = FileMgr.getFile(FrameworkName.str(), + /*openFile=*/!AutomaticImport); + if (FE && AutomaticImport) + *SuggestedModule = StringRef(Filename.begin(), SlashPos); + return FE; } @@ -315,7 +332,12 @@ const FileEntry *HeaderSearch::LookupFile( const DirectoryLookup *&CurDir, const FileEntry *CurFileEnt, SmallVectorImpl *SearchPath, - SmallVectorImpl *RelativePath) { + SmallVectorImpl *RelativePath, + StringRef *SuggestedModule) +{ + if (SuggestedModule) + *SuggestedModule = StringRef(); + // If 'Filename' is absolute, check to see if it exists and no searching. if (llvm::sys::path::is_absolute(Filename)) { CurDir = 0; @@ -400,7 +422,8 @@ const FileEntry *HeaderSearch::LookupFile( // Check each directory in sequence to see if it contains this file. for (; i != SearchDirs.size(); ++i) { const FileEntry *FE = - SearchDirs[i].LookupFile(Filename, *this, SearchPath, RelativePath); + SearchDirs[i].LookupFile(Filename, *this, SearchPath, RelativePath, + BuildingModule, SuggestedModule); if (!FE) continue; CurDir = &SearchDirs[i]; @@ -439,7 +462,8 @@ const FileEntry *HeaderSearch::LookupFile( const FileEntry *Result = LookupFile(ScratchFilename, /*isAngled=*/true, FromDir, CurDir, CurFileEnt, - SearchPath, RelativePath); + SearchPath, RelativePath, + SuggestedModule); std::pair &CacheLookup = LookupFileCache.GetOrCreateValue(Filename).getValue(); CacheLookup.second diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp index be70ceccfa56..1f54ef52c3eb 100644 --- a/clang/lib/Lex/PPDirectives.cpp +++ b/clang/lib/Lex/PPDirectives.cpp @@ -17,6 +17,7 @@ #include "clang/Lex/MacroInfo.h" #include "clang/Lex/LexDiagnostic.h" #include "clang/Lex/CodeCompletionHandler.h" +#include "clang/Lex/ModuleLoader.h" #include "clang/Lex/Pragma.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/SourceManager.h" @@ -478,7 +479,8 @@ const FileEntry *Preprocessor::LookupFile( const DirectoryLookup *FromDir, const DirectoryLookup *&CurDir, SmallVectorImpl *SearchPath, - SmallVectorImpl *RelativePath) { + SmallVectorImpl *RelativePath, + StringRef *SuggestedModule) { // If the header lookup mechanism may be relative to the current file, pass in // info about where the current file is. const FileEntry *CurFileEnt = 0; @@ -502,12 +504,13 @@ const FileEntry *Preprocessor::LookupFile( CurDir = CurDirLookup; const FileEntry *FE = HeaderInfo.LookupFile( Filename, isAngled, FromDir, CurDir, CurFileEnt, - SearchPath, RelativePath); + SearchPath, RelativePath, SuggestedModule); if (FE) return FE; // Otherwise, see if this is a subframework header. If so, this is relative // to one of the headers on the #include stack. Walk the list of the current // headers on the #include stack and pass them to HeaderInfo. + // FIXME: SuggestedModule! if (IsFileLexer()) { if ((CurFileEnt = SourceMgr.getFileEntryForID(CurPPLexer->getFileID()))) if ((FE = HeaderInfo.LookupSubframeworkHeader(Filename, CurFileEnt, @@ -1214,10 +1217,21 @@ void Preprocessor::HandleIncludeDirective(SourceLocation HashLoc, llvm::SmallString<1024> RelativePath; // We get the raw path only if we have 'Callbacks' to which we later pass // the path. + StringRef SuggestedModule; const FileEntry *File = LookupFile( Filename, isAngled, LookupFrom, CurDir, - Callbacks ? &SearchPath : NULL, Callbacks ? &RelativePath : NULL); + Callbacks ? &SearchPath : NULL, Callbacks ? &RelativePath : NULL, + AutoModuleImport? &SuggestedModule : 0); + // If we are supposed to import a module rather than including the header, + // do so now. + if (!SuggestedModule.empty()) { + TheModuleLoader.loadModule(IncludeTok.getLocation(), + Identifiers.get(SuggestedModule), + FilenameTok.getLocation()); + return; + } + // Notify the callback object that we've seen an inclusion directive. if (Callbacks) Callbacks->InclusionDirective(HashLoc, IncludeTok, Filename, isAngled, File, diff --git a/clang/lib/Lex/PPMacroExpansion.cpp b/clang/lib/Lex/PPMacroExpansion.cpp index c7b00ea5c34d..9923ae7b22aa 100644 --- a/clang/lib/Lex/PPMacroExpansion.cpp +++ b/clang/lib/Lex/PPMacroExpansion.cpp @@ -785,7 +785,7 @@ static bool EvaluateHasIncludeCommon(Token &Tok, // Search include directories. const DirectoryLookup *CurDir; const FileEntry *File = - PP.LookupFile(Filename, isAngled, LookupFrom, CurDir, NULL, NULL); + PP.LookupFile(Filename, isAngled, LookupFrom, CurDir, NULL, NULL, NULL); // Get the result value. Result = true means the file exists. bool Result = File != 0; diff --git a/clang/lib/Lex/Pragma.cpp b/clang/lib/Lex/Pragma.cpp index 92c0dd53207c..f738d1de9c71 100644 --- a/clang/lib/Lex/Pragma.cpp +++ b/clang/lib/Lex/Pragma.cpp @@ -366,7 +366,8 @@ void Preprocessor::HandlePragmaDependency(Token &DependencyTok) { // Search include directories for this file. const DirectoryLookup *CurDir; - const FileEntry *File = LookupFile(Filename, isAngled, 0, CurDir, NULL, NULL); + const FileEntry *File = LookupFile(Filename, isAngled, 0, CurDir, NULL, NULL, + NULL); if (File == 0) { if (!SuppressIncludeNotFoundError) Diag(FilenameTok, diag::err_pp_file_not_found) << Filename; diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp index db3479e8f3eb..cf63e270aa0d 100644 --- a/clang/lib/Lex/Preprocessor.cpp +++ b/clang/lib/Lex/Preprocessor.cpp @@ -133,6 +133,7 @@ void Preprocessor::Initialize(const TargetInfo &Target) { KeepComments = false; KeepMacroComments = false; SuppressIncludeNotFoundError = false; + AutoModuleImport = false; // Macro expansion is enabled. DisableMacroExpansion = false; diff --git a/clang/test/Modules/Inputs/DependsOnModule.framework/Headers/DependsOnModule.h b/clang/test/Modules/Inputs/DependsOnModule.framework/Headers/DependsOnModule.h new file mode 100644 index 000000000000..fa4069774561 --- /dev/null +++ b/clang/test/Modules/Inputs/DependsOnModule.framework/Headers/DependsOnModule.h @@ -0,0 +1,3 @@ +#include + +#define DEPENDS_ON_MODULE 1 diff --git a/clang/test/Modules/Inputs/Module.framework/Headers/Module.h b/clang/test/Modules/Inputs/Module.framework/Headers/Module.h index 0753b78cdbcb..7c7ef6ea10e9 100644 --- a/clang/test/Modules/Inputs/Module.framework/Headers/Module.h +++ b/clang/test/Modules/Inputs/Module.framework/Headers/Module.h @@ -9,3 +9,4 @@ const char *getModuleVersion(void); +alloc; @end +#define MODULE_H_MACRO 1 diff --git a/clang/test/Modules/auto-module-import.c b/clang/test/Modules/auto-module-import.c new file mode 100644 index 000000000000..018717423876 --- /dev/null +++ b/clang/test/Modules/auto-module-import.c @@ -0,0 +1,13 @@ + +// RUN: rm -rf %t +// RUN: %clang_cc1 -x objective-c -fmodule-cache-path %t -fauto-module-import -F %S/Inputs -verify %s + +#include + +#ifdef MODULE_H_MACRO +# error MODULE_H_MACRO should have been hidden +#endif + +#ifdef DEPENDS_ON_MODULE +# error DEPENDS_ON_MODULE should have been hidden +#endif