r220359 - [modules] Initial support for explicitly loading .pcm files.

Richard Smith richard-llvm at metafoo.co.uk
Tue Oct 21 19:05:47 PDT 2014


Author: rsmith
Date: Tue Oct 21 21:05:46 2014
New Revision: 220359

URL: http://llvm.org/viewvc/llvm-project?rev=220359&view=rev
Log:
[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.

Added:
    cfe/trunk/test/Modules/Inputs/explicit-build/
    cfe/trunk/test/Modules/Inputs/explicit-build/a.h
    cfe/trunk/test/Modules/Inputs/explicit-build/b.h
    cfe/trunk/test/Modules/Inputs/explicit-build/c.h
    cfe/trunk/test/Modules/Inputs/explicit-build/module.modulemap
    cfe/trunk/test/Modules/explicit-build.cpp
Modified:
    cfe/trunk/include/clang/Basic/DiagnosticFrontendKinds.td
    cfe/trunk/include/clang/Basic/DiagnosticSerializationKinds.td
    cfe/trunk/include/clang/Driver/Options.td
    cfe/trunk/include/clang/Frontend/CompilerInstance.h
    cfe/trunk/include/clang/Frontend/FrontendOptions.h
    cfe/trunk/include/clang/Serialization/ASTReader.h
    cfe/trunk/include/clang/Serialization/Module.h
    cfe/trunk/lib/Driver/Tools.cpp
    cfe/trunk/lib/Frontend/ASTUnit.cpp
    cfe/trunk/lib/Frontend/CompilerInstance.cpp
    cfe/trunk/lib/Frontend/CompilerInvocation.cpp
    cfe/trunk/lib/Frontend/FrontendAction.cpp
    cfe/trunk/lib/Serialization/ASTReader.cpp
    cfe/trunk/lib/Serialization/ASTReaderDecl.cpp
    cfe/trunk/lib/Serialization/ModuleManager.cpp
    cfe/trunk/test/Modules/resolution-change.m

Modified: cfe/trunk/include/clang/Basic/DiagnosticFrontendKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticFrontendKinds.td?rev=220359&r1=220358&r2=220359&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticFrontendKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticFrontendKinds.td Tue Oct 21 21:05:46 2014
@@ -153,7 +153,7 @@ def warn_incompatible_analyzer_plugin_ap
     InGroup<DiagGroup<"analyzer-incompatible-plugin"> >;
 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<"f
 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;

Modified: cfe/trunk/include/clang/Basic/DiagnosticSerializationKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSerializationKinds.td?rev=220359&r1=220358&r2=220359&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSerializationKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSerializationKinds.td Tue Oct 21 21:05:46 2014
@@ -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;

Modified: cfe/trunk/include/clang/Driver/Options.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Driver/Options.td?rev=220359&r1=220358&r2=220359&view=diff
==============================================================================
--- cfe/trunk/include/clang/Driver/Options.td (original)
+++ cfe/trunk/include/clang/Driver/Options.td Tue Oct 21 21:05:46 2014
@@ -641,7 +641,7 @@ def fbuild_session_file : Joined<["-"],
 def fmodules_validate_once_per_build_session : Flag<["-"], "fmodules-validate-once-per-build-session">,
   Group<i_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<i_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<["-"
 def fmodule_map_file : Joined<["-"], "fmodule-map-file=">,
   Group<f_Group>, Flags<[DriverOption,CC1Option]>, MetaVarName<"<file>">,
   HelpText<"Load this module map file">;
+def fmodule_file : Joined<["-"], "fmodule-file=">,
+  Group<f_Group>, Flags<[DriverOption,CC1Option]>,
+  HelpText<"Load this precompiled module file">, MetaVarName<"<file>">;
 def fmodules_ignore_macro : Joined<["-"], "fmodules-ignore-macro=">, Group<f_Group>, Flags<[CC1Option]>,
   HelpText<"Ignore the definition of the given macro when building and loading modules">;
 def fmodules_decluse : Flag <["-"], "fmodules-decluse">, Group<f_Group>,

Modified: cfe/trunk/include/clang/Frontend/CompilerInstance.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Frontend/CompilerInstance.h?rev=220359&r1=220358&r2=220359&view=diff
==============================================================================
--- cfe/trunk/include/clang/Frontend/CompilerInstance.h (original)
+++ cfe/trunk/include/clang/Frontend/CompilerInstance.h Tue Oct 21 21:05:46 2014
@@ -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;

Modified: cfe/trunk/include/clang/Frontend/FrontendOptions.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Frontend/FrontendOptions.h?rev=220359&r1=220358&r2=220359&view=diff
==============================================================================
--- cfe/trunk/include/clang/Frontend/FrontendOptions.h (original)
+++ cfe/trunk/include/clang/Frontend/FrontendOptions.h Tue Oct 21 21:05:46 2014
@@ -230,6 +230,10 @@ public:
   /// The list of plugins to load.
   std::vector<std::string> Plugins;
 
+  /// \brief The list of additional prebuilt module files to load before
+  /// processing the input.
+  std::vector<std::string> ModuleFiles;
+
   /// \brief The list of AST files to merge.
   std::vector<std::string> ASTMergeFiles;
 

Modified: cfe/trunk/include/clang/Serialization/ASTReader.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Serialization/ASTReader.h?rev=220359&r1=220358&r2=220359&view=diff
==============================================================================
--- cfe/trunk/include/clang/Serialization/ASTReader.h (original)
+++ cfe/trunk/include/clang/Serialization/ASTReader.h Tue Oct 21 21:05:46 2014
@@ -206,6 +206,9 @@ public:
                            std::unique_ptr<ASTReaderListener> Second)
       : First(std::move(First)), Second(std::move(Second)) {}
 
+  std::unique_ptr<ASTReaderListener> takeFirst() { return std::move(First); }
+  std::unique_ptr<ASTReaderListener> 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<ASTReaderListener> takeListener() {
+    return std::move(Listener);
+  }
+
   /// \brief Set the AST callbacks listener.
   void setListener(std::unique_ptr<ASTReaderListener> 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<ASTReaderListener> 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<ASTReaderListener> L)
+        : Reader(Reader), Chained(false) {
+      auto Old = Reader.takeListener();
+      if (Old) {
+        Chained = true;
+        L = llvm::make_unique<ChainedASTReaderListener>(std::move(L),
+                                                        std::move(Old));
+      }
+      Reader.setListener(std::move(L));
+    }
+    ~ListenerScope() {
+      auto New = Reader.takeListener();
+      if (Chained)
+        Reader.setListener(static_cast<ChainedASTReaderListener *>(New.get())
+                               ->takeSecond());
+    }
+  };
+
   /// \brief Set the AST deserialization listener.
   void setDeserializationListener(ASTDeserializationListener *Listener,
                                   bool TakeOwnership = false);

Modified: cfe/trunk/include/clang/Serialization/Module.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Serialization/Module.h?rev=220359&r1=220358&r2=220359&view=diff
==============================================================================
--- cfe/trunk/include/clang/Serialization/Module.h (original)
+++ cfe/trunk/include/clang/Serialization/Module.h Tue Oct 21 21:05:46 2014
@@ -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.

Modified: cfe/trunk/lib/Driver/Tools.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Driver/Tools.cpp?rev=220359&r1=220358&r2=220359&view=diff
==============================================================================
--- cfe/trunk/lib/Driver/Tools.cpp (original)
+++ cfe/trunk/lib/Driver/Tools.cpp Tue Oct 21 21:05:46 2014
@@ -3833,7 +3833,11 @@ void Clang::ConstructJob(Compilation &C,
   // 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();

Modified: cfe/trunk/lib/Frontend/ASTUnit.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/ASTUnit.cpp?rev=220359&r1=220358&r2=220359&view=diff
==============================================================================
--- cfe/trunk/lib/Frontend/ASTUnit.cpp (original)
+++ cfe/trunk/lib/Frontend/ASTUnit.cpp Tue Oct 21 21:05:46 2014
@@ -2776,7 +2776,8 @@ struct PCHLocatorInfo {
 static bool PCHLocator(serialization::ModuleFile &M, void *UserData) {
   PCHLocatorInfo &Info = *static_cast<PCHLocatorInfo*>(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;

Modified: cfe/trunk/lib/Frontend/CompilerInstance.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/CompilerInstance.cpp?rev=220359&r1=220358&r2=220359&view=diff
==============================================================================
--- cfe/trunk/lib/Frontend/CompilerInstance.cpp (original)
+++ cfe/trunk/lib/Frontend/CompilerInstance.cpp Tue Oct 21 21:05:46 2014
@@ -1040,7 +1040,7 @@ static bool compileAndLoadModule(Compile
     // 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::createModuleManag
 }
 
 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<void(StringRef)> OnRead;
+      ReadModuleNameListener(std::function<void(StringRef)> 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<ReadModuleNameListener>([&](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(SourceLocat
 
     // 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;
 

Modified: cfe/trunk/lib/Frontend/CompilerInvocation.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/CompilerInvocation.cpp?rev=220359&r1=220358&r2=220359&view=diff
==============================================================================
--- cfe/trunk/lib/Frontend/CompilerInvocation.cpp (original)
+++ cfe/trunk/lib/Frontend/CompilerInvocation.cpp Tue Oct 21 21:05:46 2014
@@ -838,7 +838,8 @@ static InputKind ParseFrontendArgs(Front
   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

Modified: cfe/trunk/lib/Frontend/FrontendAction.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/FrontendAction.cpp?rev=220359&r1=220358&r2=220359&view=diff
==============================================================================
--- cfe/trunk/lib/Frontend/FrontendAction.cpp (original)
+++ cfe/trunk/lib/Frontend/FrontendAction.cpp Tue Oct 21 21:05:46 2014
@@ -321,7 +321,7 @@ bool FrontendAction::BeginSourceFile(Com
     // 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<ExternalSemaSource> source, FinalReader;
@@ -383,6 +383,17 @@ bool FrontendAction::BeginSourceFile(Com
            "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() && 

Modified: cfe/trunk/lib/Serialization/ASTReader.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTReader.cpp?rev=220359&r1=220358&r2=220359&view=diff
==============================================================================
--- cfe/trunk/lib/Serialization/ASTReader.cpp (original)
+++ cfe/trunk/lib/Serialization/ASTReader.cpp Tue Oct 21 21:05:46 2014
@@ -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
       }
     }
 
-    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<unsigned, 8> 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<SourceLocation, StringRef> AST
 
   // 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(Iden
                                     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(Iden
 
 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 &
 
         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, u
       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(Record
   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::Read
     // 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 FileNam
     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);
       }
     }

Modified: cfe/trunk/lib/Serialization/ASTReaderDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTReaderDecl.cpp?rev=220359&r1=220358&r2=220359&view=diff
==============================================================================
--- cfe/trunk/lib/Serialization/ASTReaderDecl.cpp (original)
+++ cfe/trunk/lib/Serialization/ASTReaderDecl.cpp Tue Oct 21 21:05:46 2014
@@ -1194,7 +1194,7 @@ void ASTDeclReader::VisitNamespaceDecl(N
     // any other module's anonymous namespaces, so don't attach the anonymous
     // namespace at all.
     NamespaceDecl *Anon = ReadDeclAs<NamespaceDecl>(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,
       // 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<TranslationUnitDecl>(D))
           TU->setAnonymousNamespace(Anon);
         else

Modified: cfe/trunk/lib/Serialization/ModuleManager.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ModuleManager.cpp?rev=220359&r1=220358&r2=220359&view=diff
==============================================================================
--- cfe/trunk/lib/Serialization/ModuleManager.cpp (original)
+++ cfe/trunk/lib/Serialization/ModuleManager.cpp Tue Oct 21 21:05:46 2014
@@ -89,7 +89,7 @@ ModuleManager::addModule(StringRef FileN
     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.

Added: cfe/trunk/test/Modules/Inputs/explicit-build/a.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/Inputs/explicit-build/a.h?rev=220359&view=auto
==============================================================================
--- cfe/trunk/test/Modules/Inputs/explicit-build/a.h (added)
+++ cfe/trunk/test/Modules/Inputs/explicit-build/a.h Tue Oct 21 21:05:46 2014
@@ -0,0 +1,5 @@
+#if !__building_module(a)
+#error "should only get here when building module a"
+#endif
+
+const int a = 1;

Added: cfe/trunk/test/Modules/Inputs/explicit-build/b.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/Inputs/explicit-build/b.h?rev=220359&view=auto
==============================================================================
--- cfe/trunk/test/Modules/Inputs/explicit-build/b.h (added)
+++ cfe/trunk/test/Modules/Inputs/explicit-build/b.h Tue Oct 21 21:05:46 2014
@@ -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;

Added: cfe/trunk/test/Modules/Inputs/explicit-build/c.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/Inputs/explicit-build/c.h?rev=220359&view=auto
==============================================================================
--- cfe/trunk/test/Modules/Inputs/explicit-build/c.h (added)
+++ cfe/trunk/test/Modules/Inputs/explicit-build/c.h Tue Oct 21 21:05:46 2014
@@ -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;

Added: cfe/trunk/test/Modules/Inputs/explicit-build/module.modulemap
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/Inputs/explicit-build/module.modulemap?rev=220359&view=auto
==============================================================================
--- cfe/trunk/test/Modules/Inputs/explicit-build/module.modulemap (added)
+++ cfe/trunk/test/Modules/Inputs/explicit-build/module.modulemap Tue Oct 21 21:05:46 2014
@@ -0,0 +1,3 @@
+module a { header "a.h" }
+module b { header "b.h" export * }
+module c { header "c.h" export * }

Added: cfe/trunk/test/Modules/explicit-build.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/explicit-build.cpp?rev=220359&view=auto
==============================================================================
--- cfe/trunk/test/Modules/explicit-build.cpp (added)
+++ cfe/trunk/test/Modules/explicit-build.cpp Tue Oct 21 21:05:46 2014
@@ -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 at -2 {{undeclared identifier}}
+  #else
+  // expected-error at -4 {{must be imported from module 'a'}}
+  // expected-note at Inputs/explicit-build/a.h:* {{here}}
+  #endif
+
+  const int use_b = b;
+  #ifndef HAVE_B
+  // expected-error at -2 {{undeclared identifier}}
+  #else
+  // expected-error at -4 {{must be imported from module 'b'}}
+  // expected-note at Inputs/explicit-build/b.h:* {{here}}
+  #endif
+
+  const int use_c = c;
+  #ifndef HAVE_C
+  // expected-error at -2 {{undeclared identifier}}
+  #else
+  // expected-error at -4 {{must be imported from module 'c'}}
+  // expected-note at 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

Modified: cfe/trunk/test/Modules/resolution-change.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/resolution-change.m?rev=220359&r1=220358&r2=220359&view=diff
==============================================================================
--- cfe/trunk/test/Modules/resolution-change.m (original)
+++ cfe/trunk/test/Modules/resolution-change.m Tue Oct 21 21:05:46 2014
@@ -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





More information about the cfe-commits mailing list