From e842a47452223f9f3b683e0f7f9cccb48192cbb6 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Wed, 22 Oct 2014 02:05:46 +0000 Subject: [PATCH] [modules] Initial support for explicitly loading .pcm files. Implicit module builds are not well-suited to a lot of build systems. In particular, they fare badly in distributed build systems, and they lead to build artifacts that are not tracked as part of the usual dependency management process. This change allows explicitly-built module files (which are already supported through the -emit-module flag) to be explicitly loaded into a build, allowing build systems to opt to manage module builds and dependencies themselves. This is only the first step in supporting such configurations, and it should be considered experimental and subject to change or removal for now. llvm-svn: 220359 --- .../include/clang/Basic/DiagnosticFrontendKinds.td | 6 +- .../clang/Basic/DiagnosticSerializationKinds.td | 4 +- clang/include/clang/Driver/Options.td | 5 +- clang/include/clang/Frontend/CompilerInstance.h | 2 + clang/include/clang/Frontend/FrontendOptions.h | 4 + clang/include/clang/Serialization/ASTReader.h | 34 ++++- clang/include/clang/Serialization/Module.h | 9 +- clang/lib/Driver/Tools.cpp | 6 +- clang/lib/Frontend/ASTUnit.cpp | 3 +- clang/lib/Frontend/CompilerInstance.cpp | 54 +++++++- clang/lib/Frontend/CompilerInvocation.cpp | 3 +- clang/lib/Frontend/FrontendAction.cpp | 13 +- clang/lib/Serialization/ASTReader.cpp | 58 +++++--- clang/lib/Serialization/ASTReaderDecl.cpp | 5 +- clang/lib/Serialization/ModuleManager.cpp | 2 +- clang/test/Modules/Inputs/explicit-build/a.h | 5 + clang/test/Modules/Inputs/explicit-build/b.h | 7 + clang/test/Modules/Inputs/explicit-build/c.h | 7 + .../Modules/Inputs/explicit-build/module.modulemap | 3 + clang/test/Modules/explicit-build.cpp | 151 +++++++++++++++++++++ clang/test/Modules/resolution-change.m | 4 +- 21 files changed, 344 insertions(+), 41 deletions(-) create mode 100644 clang/test/Modules/Inputs/explicit-build/a.h create mode 100644 clang/test/Modules/Inputs/explicit-build/b.h create mode 100644 clang/test/Modules/Inputs/explicit-build/c.h create mode 100644 clang/test/Modules/Inputs/explicit-build/module.modulemap create mode 100644 clang/test/Modules/explicit-build.cpp diff --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td b/clang/include/clang/Basic/DiagnosticFrontendKinds.td index d58552b..90303f0 100644 --- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td +++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td @@ -153,7 +153,7 @@ def warn_incompatible_analyzer_plugin_api : Warning< InGroup >; def note_incompatible_analyzer_plugin_api : Note< "current API version is '%0', but plugin was compiled with version '%1'">; - + def err_module_map_not_found : Error<"module map file '%0' not found">, DefaultFatal; def err_missing_module_name : Error< @@ -187,6 +187,10 @@ def remark_module_build_done : Remark<"finished building module '%0'">, def err_conflicting_module_names : Error< "conflicting module names specified: '-fmodule-name=%0' and " "'-fmodule-implementation-of %1'">; +def err_module_already_loaded : Error< + "module '%0' has already been loaded; cannot load module file '%1'">; +def err_module_file_not_module : Error< + "AST file '%0' was not built as a module">, DefaultFatal; def err_missing_vfs_overlay_file : Error< "virtual filesystem overlay file '%0' not found">, DefaultFatal; diff --git a/clang/include/clang/Basic/DiagnosticSerializationKinds.td b/clang/include/clang/Basic/DiagnosticSerializationKinds.td index 1c81fc60..5de2c6a 100644 --- a/clang/include/clang/Basic/DiagnosticSerializationKinds.td +++ b/clang/include/clang/Basic/DiagnosticSerializationKinds.td @@ -52,7 +52,9 @@ def err_pch_with_compiler_errors : Error< "PCH file contains compiler errors">; def err_imported_module_not_found : Error< - "module '%0' imported by AST file '%1' not found">, DefaultFatal; + "module '%0' in AST file '%1' (imported by AST file '%2') " + "is not defined in any loaded module map file; " + "maybe you need to load '%3'?">, DefaultFatal; def err_imported_module_modmap_changed : Error< "module '%0' imported by AST file '%1' found in a different module map file" " (%2) than when the importing AST file was built (%3)">, DefaultFatal; diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 0d1a4e9..f0b384b 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -641,7 +641,7 @@ def fbuild_session_file : Joined<["-"], "fbuild-session-file=">, def fmodules_validate_once_per_build_session : Flag<["-"], "fmodules-validate-once-per-build-session">, Group, Flags<[CC1Option]>, HelpText<"Don't verify input files for the modules if the module has been " - "successfully validate or loaded during this build session">; + "successfully validated or loaded during this build session">; def fmodules_validate_system_headers : Flag<["-"], "fmodules-validate-system-headers">, Group, Flags<[CC1Option]>, HelpText<"Validate the system headers that a module depends on when loading the module">; @@ -657,6 +657,9 @@ def fmodule_name : JoinedOrSeparate<["-"], "fmodule-name=">, Group, def fmodule_map_file : Joined<["-"], "fmodule-map-file=">, Group, Flags<[DriverOption,CC1Option]>, MetaVarName<"">, HelpText<"Load this module map file">; +def fmodule_file : Joined<["-"], "fmodule-file=">, + Group, Flags<[DriverOption,CC1Option]>, + HelpText<"Load this precompiled module file">, MetaVarName<"">; def fmodules_ignore_macro : Joined<["-"], "fmodules-ignore-macro=">, Group, Flags<[CC1Option]>, HelpText<"Ignore the definition of the given macro when building and loading modules">; def fmodules_decluse : Flag <["-"], "fmodules-decluse">, Group, diff --git a/clang/include/clang/Frontend/CompilerInstance.h b/clang/include/clang/Frontend/CompilerInstance.h index 40e3069..6f15331 100644 --- a/clang/include/clang/Frontend/CompilerInstance.h +++ b/clang/include/clang/Frontend/CompilerInstance.h @@ -691,6 +691,8 @@ public: // Create module manager. void createModuleManager(); + ModuleLoadResult loadModuleFile(StringRef FileName, SourceLocation Loc); + ModuleLoadResult loadModule(SourceLocation ImportLoc, ModuleIdPath Path, Module::NameVisibilityKind Visibility, bool IsInclusionDirective) override; diff --git a/clang/include/clang/Frontend/FrontendOptions.h b/clang/include/clang/Frontend/FrontendOptions.h index 04b7c1b..aa1e253 100644 --- a/clang/include/clang/Frontend/FrontendOptions.h +++ b/clang/include/clang/Frontend/FrontendOptions.h @@ -230,6 +230,10 @@ public: /// The list of plugins to load. std::vector Plugins; + /// \brief The list of additional prebuilt module files to load before + /// processing the input. + std::vector ModuleFiles; + /// \brief The list of AST files to merge. std::vector ASTMergeFiles; diff --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h index 1b3d46a..730a257 100644 --- a/clang/include/clang/Serialization/ASTReader.h +++ b/clang/include/clang/Serialization/ASTReader.h @@ -206,6 +206,9 @@ public: std::unique_ptr Second) : First(std::move(First)), Second(std::move(Second)) {} + std::unique_ptr takeFirst() { return std::move(First); } + std::unique_ptr takeSecond() { return std::move(Second); } + bool ReadFullVersionInformation(StringRef FullVersion) override; void ReadModuleName(StringRef ModuleName) override; void ReadModuleMapFile(StringRef ModuleMapPath) override; @@ -1389,12 +1392,17 @@ public: void makeNamesVisible(const HiddenNames &Names, Module *Owner, bool FromFinalization); + /// \brief Take the AST callbacks listener. + std::unique_ptr takeListener() { + return std::move(Listener); + } + /// \brief Set the AST callbacks listener. void setListener(std::unique_ptr Listener) { this->Listener = std::move(Listener); } - /// \brief Add an AST callbak listener. + /// \brief Add an AST callback listener. /// /// Takes ownership of \p L. void addListener(std::unique_ptr L) { @@ -1404,6 +1412,30 @@ public: Listener = std::move(L); } + /// RAII object to temporarily add an AST callback listener. + class ListenerScope { + ASTReader &Reader; + bool Chained; + + public: + ListenerScope(ASTReader &Reader, std::unique_ptr L) + : Reader(Reader), Chained(false) { + auto Old = Reader.takeListener(); + if (Old) { + Chained = true; + L = llvm::make_unique(std::move(L), + std::move(Old)); + } + Reader.setListener(std::move(L)); + } + ~ListenerScope() { + auto New = Reader.takeListener(); + if (Chained) + Reader.setListener(static_cast(New.get()) + ->takeSecond()); + } + }; + /// \brief Set the AST deserialization listener. void setDeserializationListener(ASTDeserializationListener *Listener, bool TakeOwnership = false); diff --git a/clang/include/clang/Serialization/Module.h b/clang/include/clang/Serialization/Module.h index 4952039..a889e8b 100644 --- a/clang/include/clang/Serialization/Module.h +++ b/clang/include/clang/Serialization/Module.h @@ -42,10 +42,11 @@ namespace reader { /// \brief Specifies the kind of module that has been loaded. enum ModuleKind { - MK_Module, ///< File is a module proper. - MK_PCH, ///< File is a PCH file treated as such. - MK_Preamble, ///< File is a PCH file treated as the preamble. - MK_MainFile ///< File is a PCH file treated as the actual main file. + MK_ImplicitModule, ///< File is an implicitly-loaded module. + MK_ExplicitModule, ///< File is an explicitly-loaded module. + MK_PCH, ///< File is a PCH file treated as such. + MK_Preamble, ///< File is a PCH file treated as the preamble. + MK_MainFile ///< File is a PCH file treated as the actual main file. }; /// \brief Information about the contents of a DeclContext. diff --git a/clang/lib/Driver/Tools.cpp b/clang/lib/Driver/Tools.cpp index 822d55b..d8f3edd 100644 --- a/clang/lib/Driver/Tools.cpp +++ b/clang/lib/Driver/Tools.cpp @@ -3833,7 +3833,11 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, // definitions. Args.AddAllArgs(CmdArgs, options::OPT_fmodule_map_file); - // -fmodule-cache-path specifies where our module files should be written. + // -fmodule-file can be used to specify files containing precompiled modules. + Args.AddAllArgs(CmdArgs, options::OPT_fmodule_file); + + // -fmodule-cache-path specifies where our implicitly-built module files + // should be written. SmallString<128> ModuleCachePath; if (Arg *A = Args.getLastArg(options::OPT_fmodules_cache_path)) ModuleCachePath = A->getValue(); diff --git a/clang/lib/Frontend/ASTUnit.cpp b/clang/lib/Frontend/ASTUnit.cpp index 8742a54..19fafa3 100644 --- a/clang/lib/Frontend/ASTUnit.cpp +++ b/clang/lib/Frontend/ASTUnit.cpp @@ -2776,7 +2776,8 @@ struct PCHLocatorInfo { static bool PCHLocator(serialization::ModuleFile &M, void *UserData) { PCHLocatorInfo &Info = *static_cast(UserData); switch (M.Kind) { - case serialization::MK_Module: + case serialization::MK_ImplicitModule: + case serialization::MK_ExplicitModule: return true; // skip dependencies. case serialization::MK_PCH: Info.Mod = &M; diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp index e1803d0..ba0743a 100644 --- a/clang/lib/Frontend/CompilerInstance.cpp +++ b/clang/lib/Frontend/CompilerInstance.cpp @@ -1040,7 +1040,7 @@ static bool compileAndLoadModule(CompilerInstance &ImportingInstance, // Try to read the module file, now that we've compiled it. ASTReader::ASTReadResult ReadResult = ImportingInstance.getModuleManager()->ReadAST( - ModuleFileName, serialization::MK_Module, ImportLoc, + ModuleFileName, serialization::MK_ImplicitModule, ImportLoc, ModuleLoadCapabilities); if (ReadResult == ASTReader::OutOfDate && @@ -1268,6 +1268,53 @@ void CompilerInstance::createModuleManager() { } ModuleLoadResult +CompilerInstance::loadModuleFile(StringRef FileName, SourceLocation Loc) { + if (!ModuleManager) + createModuleManager(); + if (!ModuleManager) + return ModuleLoadResult(); + + // Load the module if this is the first time we've been told about this file. + auto *MF = ModuleManager->getModuleManager().lookup(FileName); + if (!MF) { + struct ReadModuleNameListener : ASTReaderListener { + std::function OnRead; + ReadModuleNameListener(std::function F) : OnRead(F) {} + void ReadModuleName(StringRef ModuleName) override { OnRead(ModuleName); } + }; + + // Register listener to track the modules that are loaded by explicitly + // loading a module file. We suppress any attempts to implicitly load + // module files for any such module. + ASTReader::ListenerScope OnReadModuleName( + *ModuleManager, + llvm::make_unique([&](StringRef ModuleName) { + auto &PP = getPreprocessor(); + auto *NameII = PP.getIdentifierInfo(ModuleName); + auto *Module = PP.getHeaderSearchInfo().lookupModule(ModuleName, false); + if (!KnownModules.insert(std::make_pair(NameII, Module)).second) + getDiagnostics().Report(Loc, diag::err_module_already_loaded) + << ModuleName << FileName; + })); + + if (ModuleManager->ReadAST(FileName, serialization::MK_ExplicitModule, Loc, + ASTReader::ARR_None) != ASTReader::Success) + return ModuleLoadResult(); + + MF = ModuleManager->getModuleManager().lookup(FileName); + assert(MF && "unexpectedly failed to load module file"); + } + + if (MF->ModuleName.empty()) { + getDiagnostics().Report(Loc, diag::err_module_file_not_module) + << FileName; + return ModuleLoadResult(); + } + auto *Module = PP->getHeaderSearchInfo().lookupModule(MF->ModuleName, false); + return ModuleLoadResult(Module, false); +} + +ModuleLoadResult CompilerInstance::loadModule(SourceLocation ImportLoc, ModuleIdPath Path, Module::NameVisibilityKind Visibility, @@ -1330,8 +1377,9 @@ CompilerInstance::loadModule(SourceLocation ImportLoc, // Try to load the module file. unsigned ARRFlags = ASTReader::ARR_OutOfDate | ASTReader::ARR_Missing; - switch (ModuleManager->ReadAST(ModuleFileName, serialization::MK_Module, - ImportLoc, ARRFlags)) { + switch (ModuleManager->ReadAST(ModuleFileName, + serialization::MK_ImplicitModule, ImportLoc, + ARRFlags)) { case ASTReader::Success: break; diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 373842d..2c4118c 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -838,7 +838,8 @@ static InputKind ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args, Opts.ASTDumpLookups = Args.hasArg(OPT_ast_dump_lookups); Opts.UseGlobalModuleIndex = !Args.hasArg(OPT_fno_modules_global_index); Opts.GenerateGlobalModuleIndex = Opts.UseGlobalModuleIndex; - + Opts.ModuleFiles = Args.getAllArgValues(OPT_fmodule_file); + Opts.CodeCompleteOpts.IncludeMacros = Args.hasArg(OPT_code_completion_macros); Opts.CodeCompleteOpts.IncludeCodePatterns diff --git a/clang/lib/Frontend/FrontendAction.cpp b/clang/lib/Frontend/FrontendAction.cpp index 8760f50..1c93842 100644 --- a/clang/lib/Frontend/FrontendAction.cpp +++ b/clang/lib/Frontend/FrontendAction.cpp @@ -321,7 +321,7 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI, // FIXME: should not overwrite ASTMutationListener when parsing model files? if (!isModelParsingAction()) CI.getASTContext().setASTMutationListener(Consumer->GetASTMutationListener()); - + if (!CI.getPreprocessorOpts().ChainedIncludes.empty()) { // Convert headers to PCH and chain them. IntrusiveRefCntPtr source, FinalReader; @@ -383,6 +383,17 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI, "doesn't support modules"); } + // If we were asked to load any module files, do so now. Don't make any names + // from those modules visible. + for (const auto &ModuleFile : CI.getFrontendOpts().ModuleFiles) { + // FIXME: Use a better source location here. Perhaps inject something + // into the predefines buffer to represent these module files. + if (!CI.loadModuleFile(ModuleFile, + CI.getSourceManager().getLocForStartOfFile( + CI.getSourceManager().getMainFileID()))) + goto failure; + } + // If there is a layout overrides file, attach an external AST source that // provides the layouts from that file. if (!CI.getFrontendOpts().OverrideRecordLayoutsFile.empty() && diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index 3a6e655..f318154 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -389,14 +389,14 @@ bool PCHValidator::ReadDiagnosticOptions( // If the original import came from a file explicitly generated by the user, // don't check the diagnostic mappings. // FIXME: currently this is approximated by checking whether this is not a - // module import. + // module import of an implicitly-loaded module file. // Note: ModuleMgr.rbegin() may not be the current module, but it must be in // the transitive closure of its imports, since unrelated modules cannot be // imported until after this module finishes validation. ModuleFile *TopImport = *ModuleMgr.rbegin(); while (!TopImport->ImportedBy.empty()) TopImport = TopImport->ImportedBy[0]; - if (TopImport->Kind != MK_Module) + if (TopImport->Kind != MK_ImplicitModule) return false; StringRef ModuleName = TopImport->ModuleName; @@ -781,7 +781,7 @@ IdentifierInfo *ASTIdentifierLookupTrait::ReadData(const internal_key_type& k, } } - if (F.Kind == MK_Module) { + if (F.Kind == MK_ImplicitModule || F.Kind == MK_ExplicitModule) { // Macro definitions are stored from newest to oldest, so reverse them // before registering them. llvm::SmallVector MacroSizes; @@ -1238,7 +1238,8 @@ bool ASTReader::ReadSLocEntry(int ID) { SrcMgr::CharacteristicKind FileCharacter = (SrcMgr::CharacteristicKind)Record[2]; SourceLocation IncludeLoc = ReadSourceLocation(*F, Record[1]); - if (IncludeLoc.isInvalid() && F->Kind == MK_Module) { + if (IncludeLoc.isInvalid() && + (F->Kind == MK_ImplicitModule || F->Kind == MK_ExplicitModule)) { IncludeLoc = getImportLocation(F); } unsigned Code = SLocEntryCursor.ReadCode(); @@ -1284,7 +1285,7 @@ std::pair ASTReader::getModuleImportLoc(int ID) { // Find which module file this entry lands in. ModuleFile *M = GlobalSLocEntryMap.find(-ID)->second; - if (M->Kind != MK_Module) + if (M->Kind != MK_ImplicitModule && M->Kind != MK_ExplicitModule) return std::make_pair(SourceLocation(), ""); // FIXME: Can we map this down to a particular submodule? That would be @@ -1773,7 +1774,8 @@ void ASTReader::resolvePendingMacro(IdentifierInfo *II, const PendingMacroInfo &PMInfo) { assert(II); - if (PMInfo.M->Kind != MK_Module) { + if (PMInfo.M->Kind != MK_ImplicitModule && + PMInfo.M->Kind != MK_ExplicitModule) { installPCHMacroDirectives(II, *PMInfo.M, PMInfo.PCHMacroData.MacroDirectivesOffset); return; @@ -1797,7 +1799,7 @@ void ASTReader::resolvePendingMacro(IdentifierInfo *II, void ASTReader::installPCHMacroDirectives(IdentifierInfo *II, ModuleFile &M, uint64_t Offset) { - assert(M.Kind != MK_Module); + assert(M.Kind != MK_ImplicitModule && M.Kind != MK_ExplicitModule); BitstreamCursor &Cursor = M.MacroCursor; SavedStreamPosition SavedPosition(Cursor); @@ -2279,7 +2281,8 @@ ASTReader::ReadControlBlock(ModuleFile &F, unsigned N = NumUserInputs; if (ValidateSystemInputs || - (HSOpts.ModulesValidateOncePerBuildSession && F.Kind == MK_Module)) + (HSOpts.ModulesValidateOncePerBuildSession && + F.Kind == MK_ImplicitModule)) N = NumInputs; for (unsigned I = 0; I < N; ++I) { @@ -3191,7 +3194,7 @@ ASTReader::ReadASTBlock(ModuleFile &F, unsigned ClientLoadCapabilities) { break; case IMPORTED_MODULES: { - if (F.Kind != MK_Module) { + if (F.Kind != MK_ImplicitModule && F.Kind != MK_ExplicitModule) { // If we aren't loading a module (which has its own exports), make // all of the imported modules visible. // FIXME: Deal with macros-only imports. @@ -3289,29 +3292,40 @@ ASTReader::ReadModuleMapFileBlock(RecordData &Record, ModuleFile &F, unsigned Idx = 0; F.ModuleMapPath = ReadString(Record, Idx); + if (F.Kind == MK_ExplicitModule) { + // For an explicitly-loaded module, we don't care whether the original + // module map file exists or matches. + return Success; + } + // Try to resolve ModuleName in the current header search context and // verify that it is found in the same module map file as we saved. If the // top-level AST file is a main file, skip this check because there is no // usable header search context. assert(!F.ModuleName.empty() && - "MODULE_NAME should come before MOUDLE_MAP_FILE"); - if (F.Kind == MK_Module && (*ModuleMgr.begin())->Kind != MK_MainFile) { + "MODULE_NAME should come before MODULE_MAP_FILE"); + if (F.Kind == MK_ImplicitModule && + (*ModuleMgr.begin())->Kind != MK_MainFile) { + // An implicitly-loaded module file should have its module listed in some + // module map file that we've already loaded. Module *M = PP.getHeaderSearchInfo().lookupModule(F.ModuleName); - if (!M) { + auto &Map = PP.getHeaderSearchInfo().getModuleMap(); + const FileEntry *ModMap = M ? Map.getModuleMapFileForUniquing(M) : nullptr; + if (!ModMap) { assert(ImportedBy && "top-level import should be verified"); if ((ClientLoadCapabilities & ARR_Missing) == 0) - Diag(diag::err_imported_module_not_found) - << F.ModuleName << ImportedBy->FileName; + Diag(diag::err_imported_module_not_found) << F.ModuleName << F.FileName + << ImportedBy->FileName + << F.ModuleMapPath; return Missing; } + assert(M->Name == F.ModuleName && "found module with different name"); + // Check the primary module map file. - auto &Map = PP.getHeaderSearchInfo().getModuleMap(); const FileEntry *StoredModMap = FileMgr.getFile(F.ModuleMapPath); - const FileEntry *ModMap = Map.getModuleMapFileForUniquing(M); if (StoredModMap == nullptr || StoredModMap != ModMap) { assert(ModMap && "found module is missing module map file"); - assert(M->Name == F.ModuleName && "found module with different name"); assert(ImportedBy && "top-level import should be verified"); if ((ClientLoadCapabilities & ARR_OutOfDate) == 0) Diag(diag::err_imported_module_modmap_changed) @@ -3696,7 +3710,7 @@ ASTReader::ASTReadResult ASTReader::ReadAST(const std::string &FileName, // in the filesystem). for (unsigned I = 0, N = Loaded.size(); I != N; ++I) { ImportedModule &M = Loaded[I]; - if (M.Mod->Kind == MK_Module) { + if (M.Mod->Kind == MK_ImplicitModule) { updateModuleTimestamp(*M.Mod); } } @@ -3729,7 +3743,7 @@ ASTReader::ReadASTCore(StringRef FileName, break; case ModuleManager::Missing: - // The module file was missing; if the client handle handle, that, return + // The module file was missing; if the client can handle that, return // it. if (ClientLoadCapabilities & ARR_Missing) return Missing; @@ -8196,14 +8210,16 @@ void ASTReader::finishPendingActions() { for (unsigned IDIdx = 0, NumIDs = GlobalIDs.size(); IDIdx != NumIDs; ++IDIdx) { const PendingMacroInfo &Info = GlobalIDs[IDIdx]; - if (Info.M->Kind != MK_Module) + if (Info.M->Kind != MK_ImplicitModule && + Info.M->Kind != MK_ExplicitModule) resolvePendingMacro(II, Info); } // Handle module imports. for (unsigned IDIdx = 0, NumIDs = GlobalIDs.size(); IDIdx != NumIDs; ++IDIdx) { const PendingMacroInfo &Info = GlobalIDs[IDIdx]; - if (Info.M->Kind == MK_Module) + if (Info.M->Kind == MK_ImplicitModule || + Info.M->Kind == MK_ExplicitModule) resolvePendingMacro(II, Info); } } diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index a3b773f..b5a447d 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -1194,7 +1194,7 @@ void ASTDeclReader::VisitNamespaceDecl(NamespaceDecl *D) { // any other module's anonymous namespaces, so don't attach the anonymous // namespace at all. NamespaceDecl *Anon = ReadDeclAs(Record, Idx); - if (F.Kind != MK_Module) + if (F.Kind != MK_ImplicitModule && F.Kind != MK_ExplicitModule) D->setAnonymousNamespace(Anon); } else { // Link this namespace back to the first declaration, which has already @@ -3536,7 +3536,8 @@ void ASTDeclReader::UpdateDecl(Decl *D, ModuleFile &ModuleFile, // Each module has its own anonymous namespace, which is disjoint from // any other module's anonymous namespaces, so don't attach the anonymous // namespace at all. - if (ModuleFile.Kind != MK_Module) { + if (ModuleFile.Kind != MK_ImplicitModule && + ModuleFile.Kind != MK_ExplicitModule) { if (TranslationUnitDecl *TU = dyn_cast(D)) TU->setAnonymousNamespace(Anon); else diff --git a/clang/lib/Serialization/ModuleManager.cpp b/clang/lib/Serialization/ModuleManager.cpp index 18fe035..20249e0 100644 --- a/clang/lib/Serialization/ModuleManager.cpp +++ b/clang/lib/Serialization/ModuleManager.cpp @@ -89,7 +89,7 @@ ModuleManager::addModule(StringRef FileName, ModuleKind Type, ModuleEntry = New; New->InputFilesValidationTimestamp = 0; - if (New->Kind == MK_Module) { + if (New->Kind == MK_ImplicitModule) { std::string TimestampFilename = New->getTimestampFilename(); vfs::Status Status; // A cached stat value would be fine as well. diff --git a/clang/test/Modules/Inputs/explicit-build/a.h b/clang/test/Modules/Inputs/explicit-build/a.h new file mode 100644 index 0000000..5e3602f --- /dev/null +++ b/clang/test/Modules/Inputs/explicit-build/a.h @@ -0,0 +1,5 @@ +#if !__building_module(a) +#error "should only get here when building module a" +#endif + +const int a = 1; diff --git a/clang/test/Modules/Inputs/explicit-build/b.h b/clang/test/Modules/Inputs/explicit-build/b.h new file mode 100644 index 0000000..449b385 --- /dev/null +++ b/clang/test/Modules/Inputs/explicit-build/b.h @@ -0,0 +1,7 @@ +#include "a.h" + +#if !__building_module(b) +#error "should only get here when building module b" +#endif + +const int b = 2; diff --git a/clang/test/Modules/Inputs/explicit-build/c.h b/clang/test/Modules/Inputs/explicit-build/c.h new file mode 100644 index 0000000..2c66a23 --- /dev/null +++ b/clang/test/Modules/Inputs/explicit-build/c.h @@ -0,0 +1,7 @@ +#include "b.h" + +#if !__building_module(c) +#error "should only get here when building module c" +#endif + +const int c = 3; diff --git a/clang/test/Modules/Inputs/explicit-build/module.modulemap b/clang/test/Modules/Inputs/explicit-build/module.modulemap new file mode 100644 index 0000000..bd6ea83 --- /dev/null +++ b/clang/test/Modules/Inputs/explicit-build/module.modulemap @@ -0,0 +1,3 @@ +module a { header "a.h" } +module b { header "b.h" export * } +module c { header "c.h" export * } diff --git a/clang/test/Modules/explicit-build.cpp b/clang/test/Modules/explicit-build.cpp new file mode 100644 index 0000000..0911837 --- /dev/null +++ b/clang/test/Modules/explicit-build.cpp @@ -0,0 +1,151 @@ +// RUN: rm -rf %t + +// ------------------------------- +// Build chained modules A, B, and C +// RUN: %clang_cc1 -x c++ -fmodules -fmodules-cache-path=%t -Rmodule-build -fno-modules-error-recovery \ +// RUN: -fmodule-name=a -emit-module %S/Inputs/explicit-build/module.modulemap -o %t/a.pcm \ +// RUN: 2>&1 | FileCheck --check-prefix=CHECK-NO-IMPLICIT-BUILD %s --allow-empty +// +// RUN: %clang_cc1 -x c++ -fmodules -fmodules-cache-path=%t -Rmodule-build -fno-modules-error-recovery \ +// RUN: -fmodule-file=%t/a.pcm \ +// RUN: -fmodule-name=b -emit-module %S/Inputs/explicit-build/module.modulemap -o %t/b.pcm \ +// RUN: 2>&1 | FileCheck --check-prefix=CHECK-NO-IMPLICIT-BUILD %s --allow-empty +// +// RUN: %clang_cc1 -x c++ -fmodules -fmodules-cache-path=%t -Rmodule-build -fno-modules-error-recovery \ +// RUN: -fmodule-file=%t/b.pcm \ +// RUN: -fmodule-name=c -emit-module %S/Inputs/explicit-build/module.modulemap -o %t/c.pcm \ +// RUN: 2>&1 | FileCheck --check-prefix=CHECK-NO-IMPLICIT-BUILD %s --allow-empty +// +// CHECK-NO-IMPLICIT-BUILD-NOT: building module + +// ------------------------------- +// Build B with an implicit build of A +// RUN: %clang_cc1 -x c++ -fmodules -fmodules-cache-path=%t -Rmodule-build -fno-modules-error-recovery \ +// RUN: -fmodule-name=b -emit-module %S/Inputs/explicit-build/module.modulemap -o %t/b-not-a.pcm \ +// RUN: 2>&1 | FileCheck --check-prefix=CHECK-B-NO-A %s +// +// CHECK-B-NO-A: While building module 'b': +// CHECK-B-NO-A: building module 'a' as + +// ------------------------------- +// Check that we can use the explicitly-built A, B, and C modules. +// RUN: %clang_cc1 -x c++ -fmodules -fmodules-cache-path=%t -Rmodule-build -fno-modules-error-recovery \ +// RUN: -fmodule-file=%t/a.pcm \ +// RUN: -verify %s -DHAVE_A +// +// RUN: %clang_cc1 -x c++ -fmodules -fmodules-cache-path=%t -Rmodule-build -fno-modules-error-recovery \ +// RUN: -fmodule-file=%t/a.pcm \ +// RUN: -fmodule-map-file=%S/Inputs/explicit-build/module.modulemap \ +// RUN: -verify %s -DHAVE_A +// +// RUN: %clang_cc1 -x c++ -fmodules -fmodules-cache-path=%t -Rmodule-build -fno-modules-error-recovery \ +// RUN: -fmodule-file=%t/b.pcm \ +// RUN: -verify %s -DHAVE_A -DHAVE_B +// +// RUN: %clang_cc1 -x c++ -fmodules -fmodules-cache-path=%t -Rmodule-build -fno-modules-error-recovery \ +// RUN: -fmodule-file=%t/a.pcm \ +// RUN: -fmodule-file=%t/b.pcm \ +// RUN: -verify %s -DHAVE_A -DHAVE_B +// +// RUN: %clang_cc1 -x c++ -fmodules -fmodules-cache-path=%t -Rmodule-build -fno-modules-error-recovery \ +// RUN: -fmodule-file=%t/a.pcm \ +// RUN: -fmodule-file=%t/b.pcm \ +// RUN: -fmodule-file=%t/c.pcm \ +// RUN: -verify %s -DHAVE_A -DHAVE_B -DHAVE_C +// +// RUN: %clang_cc1 -x c++ -fmodules -fmodules-cache-path=%t -Rmodule-build -fno-modules-error-recovery \ +// RUN: -I%S/Inputs/explicit-build \ +// RUN: -fmodule-file=%t/a.pcm \ +// RUN: -fmodule-file=%t/b.pcm \ +// RUN: -fmodule-file=%t/c.pcm \ +// RUN: -verify %s -INCLUDE_ALL -DHAVE_A -DHAVE_B -DHAVE_C + +#ifdef INCLUDE_ALL + #include "a.h" + #include "b.h" + #include "c.h" + static_assert(a == 1, ""); + static_assert(b == 2, ""); + static_assert(c == 3, ""); +#else + const int use_a = a; + #ifndef HAVE_A + // expected-error@-2 {{undeclared identifier}} + #else + // expected-error@-4 {{must be imported from module 'a'}} + // expected-note@Inputs/explicit-build/a.h:* {{here}} + #endif + + const int use_b = b; + #ifndef HAVE_B + // expected-error@-2 {{undeclared identifier}} + #else + // expected-error@-4 {{must be imported from module 'b'}} + // expected-note@Inputs/explicit-build/b.h:* {{here}} + #endif + + const int use_c = c; + #ifndef HAVE_C + // expected-error@-2 {{undeclared identifier}} + #else + // expected-error@-4 {{must be imported from module 'c'}} + // expected-note@Inputs/explicit-build/c.h:* {{here}} + #endif +#endif + +// ------------------------------- +// Check that we can use a mixture of implicit and explicit modules. +// RUN: not %clang_cc1 -x c++ -fmodules -fmodules-cache-path=%t -Rmodule-build -fno-modules-error-recovery \ +// RUN: -fmodule-file=%t/b-not-a.pcm \ +// RUN: -fmodule-map-file=%S/Inputs/explicit-build/module.modulemap \ +// RUN: %s 2>&1 | FileCheck --check-prefix=CHECK-A-AND-B-NO-A %s + +// ------------------------------- +// Check that mixing an implicit and explicit form of the 'a' module is rejected. +// RUN: not %clang_cc1 -x c++ -fmodules -fmodules-cache-path=%t -Rmodule-build -fno-modules-error-recovery \ +// RUN: -fmodule-file=%t/a.pcm \ +// RUN: -fmodule-file=%t/b-not-a.pcm \ +// RUN: %s 2>&1 | FileCheck --check-prefix=CHECK-A-AND-B-NO-A %s +// +// RUN: not %clang_cc1 -x c++ -fmodules -fmodules-cache-path=%t -Rmodule-build -fno-modules-error-recovery \ +// RUN: -fmodule-file=%t/a.pcm \ +// RUN: -fmodule-file=%t/b-not-a.pcm \ +// RUN: -fmodule-map-file=%S/Inputs/explicit-build/module.modulemap \ +// RUN: %s 2>&1 | FileCheck --check-prefix=CHECK-A-AND-B-NO-A %s +// +// FIXME: We should load module map files specified on the command line and +// module map files in include paths on demand to allow this, and possibly +// also the previous case. +// CHECK-A-AND-B-NO-A: fatal error: module 'a' {{.*}} is not defined in any loaded module map + +// ------------------------------- +// Try to use two different flavors of the 'a' module. +// RUN: %clang_cc1 -x c++ -fmodules -fmodules-cache-path=%t -Rmodule-build -fno-modules-error-recovery \ +// RUN: -fmodule-name=a -emit-module %S/Inputs/explicit-build/module.modulemap -o %t/a-alt.pcm \ +// RUN: 2>&1 | FileCheck --check-prefix=CHECK-NO-IMPLICIT-BUILD %s --allow-empty +// +// RUN: not %clang_cc1 -x c++ -fmodules -fmodules-cache-path=%t -Rmodule-build -fno-modules-error-recovery \ +// RUN: -fmodule-file=%t/a.pcm \ +// RUN: -fmodule-file=%t/a-alt.pcm \ +// RUN: -fmodule-map-file=%S/Inputs/explicit-build/module.modulemap \ +// RUN: %s 2>&1 | FileCheck --check-prefix=CHECK-MULTIPLE-AS %s +// +// RUN: not %clang_cc1 -x c++ -fmodules -fmodules-cache-path=%t -Rmodule-build -fno-modules-error-recovery \ +// RUN: -fmodule-file=%t/a-alt.pcm \ +// RUN: -fmodule-file=%t/a.pcm \ +// RUN: -fmodule-map-file=%S/Inputs/explicit-build/module.modulemap \ +// RUN: %s 2>&1 | FileCheck --check-prefix=CHECK-MULTIPLE-AS %s +// +// CHECK-MULTIPLE-AS: error: module 'a' has already been loaded; cannot load module file '{{.*a(-alt)?}}.pcm' + +// ------------------------------- +// Try to import a PCH with -fmodule-file= +// RUN: %clang_cc1 -x c++ -fmodules -fmodules-cache-path=%t -Rmodule-build -fno-modules-error-recovery \ +// RUN: -fmodule-name=a -emit-pch %S/Inputs/explicit-build/a.h -o %t/a.pch \ +// RUN: 2>&1 | FileCheck --check-prefix=CHECK-NO-IMPLICIT-BUILD %s --allow-empty +// +// RUN: not %clang_cc1 -x c++ -fmodules -fmodules-cache-path=%t -Rmodule-build -fno-modules-error-recovery \ +// RUN: -fmodule-file=%t/a.pch \ +// RUN: %s 2>&1 | FileCheck --check-prefix=CHECK-A-AS-PCH %s +// +// CHECK-A-AS-PCH: fatal error: AST file '{{.*}}a.pch' was not built as a module diff --git a/clang/test/Modules/resolution-change.m b/clang/test/Modules/resolution-change.m index 011782e..b725a64 100644 --- a/clang/test/Modules/resolution-change.m +++ b/clang/test/Modules/resolution-change.m @@ -11,11 +11,11 @@ // Use the PCH with no way to resolve DependsOnA // RUN: not %clang_cc1 -fmodules -fmodules-cache-path=%t -include-pch %t-A.pch %s -fsyntax-only 2>&1 | FileCheck -check-prefix=CHECK-NODOA %s -// CHECK-NODOA: module 'DependsOnA' imported by AST file '{{.*A.pch}}' not found +// CHECK-NODOA: module 'DependsOnA' in AST file '{{.*DependsOnA.*pcm}}' (imported by AST file '{{.*A.pch}}') is not defined in any loaded module map // Use the PCH with no way to resolve A // RUN: not %clang_cc1 -fmodules -fmodules-cache-path=%t -I %S/Inputs/modules-with-same-name/DependsOnA -include-pch %t-A.pch %s -fsyntax-only 2>&1 | FileCheck -check-prefix=CHECK-NOA %s -// CHECK-NOA: module 'A' imported by AST file '{{.*DependsOnA.*pcm}}' not found +// CHECK-NOA: module 'A' in AST file '{{.*A.*pcm}}' (imported by AST file '{{.*DependsOnA.*pcm}}') is not defined in any loaded module map // Use the PCH and have it resolve the the other A // RUN: not %clang_cc1 -fmodules -fmodules-cache-path=%t -I %S/Inputs/modules-with-same-name/DependsOnA -I %S/Inputs/modules-with-same-name/path2/A -include-pch %t-A.pch %s -fsyntax-only 2>&1 | FileCheck -check-prefix=CHECK-WRONGA %s -- 2.7.4