]> granicus.if.org Git - clang/commitdiff
C++ Modules TS: add frontend support for building pcm files from module
authorRichard Smith <richard-llvm@metafoo.co.uk>
Fri, 26 Aug 2016 00:14:38 +0000 (00:14 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Fri, 26 Aug 2016 00:14:38 +0000 (00:14 +0000)
interface files. At the moment, all declarations (and no macros) are exported,
and 'export' declarations are not supported yet.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@279794 91177308-0d34-0410-b5e6-96231b3b80d8

27 files changed:
include/clang/Basic/DiagnosticFrontendKinds.td
include/clang/Basic/DiagnosticParseKinds.td
include/clang/Basic/DiagnosticSemaKinds.td
include/clang/Basic/LangOptions.def
include/clang/Basic/LangOptions.h
include/clang/Driver/CC1Options.td
include/clang/Frontend/FrontendActions.h
include/clang/Frontend/FrontendOptions.h
include/clang/Lex/ModuleMap.h
lib/Frontend/ASTUnit.cpp
lib/Frontend/CompilerInstance.cpp
lib/Frontend/CompilerInvocation.cpp
lib/Frontend/FrontendActions.cpp
lib/Frontend/FrontendOptions.cpp
lib/FrontendTool/ExecuteCompilerInvocation.cpp
lib/Lex/ModuleMap.cpp
lib/Lex/PPLexerChange.cpp
lib/Lex/PPMacroExpansion.cpp
lib/Lex/Preprocessor.cpp
lib/Parse/ParseAST.cpp
lib/Parse/Parser.cpp
lib/Sema/SemaDecl.cpp
lib/Sema/SemaDeclObjC.cpp
lib/Serialization/GeneratePCH.cpp
test/Parser/cxx-modules-import.cpp
test/Parser/cxx-modules-interface.cppm [new file with mode: 0644]
test/lit.cfg

index 2aa8f103500dcd08204ce3f406951d38906bf02c..43b5dfc83de6fb217e7d9ecbaa2d502e9df903b4 100644 (file)
@@ -174,6 +174,8 @@ def warn_incompatible_analyzer_plugin_api : Warning<
 def note_incompatible_analyzer_plugin_api : Note<
     "current API version is '%0', but plugin was compiled with version '%1'">;
 
+def err_module_interface_requires_modules_ts : Error<
+  "module interface compilation requires '-fmodules-ts'">;
 def warn_module_config_mismatch : Warning<
   "module file %0 cannot be loaded due to a configuration mismatch with the current "
   "compilation">, InGroup<DiagGroup<"module-file-config-mismatch">>, DefaultError;
@@ -217,4 +219,4 @@ def err_invalid_vfs_overlay : Error<
 
 def warn_option_invalid_ocl_version : Warning<
   "OpenCL version %0 does not support the option '%1'">, InGroup<Deprecated>;
-}
\ No newline at end of file
+}
index 34280b902e39a47dacad51e218401aa4692517c0..513820727a0b725e1ff942dd62cf5ee55487994f 100644 (file)
@@ -1021,6 +1021,10 @@ def warn_pragma_unroll_cuda_value_in_parens : Warning<
 } // end of Parse Issue category.
 
 let CategoryName = "Modules Issue" in {
+def err_expected_module_interface_decl : Error<
+  "expected module declaration at start of module interface">;
+def err_unexpected_module_decl : Error<
+  "module declaration must be the first declaration in the translation unit">;
 def err_module_expected_ident : Error<
   "expected a module name after module%select{| import}0">;
 def err_unexpected_module_kind : Error<
index bf6e6ecab9aa42262122c056d99977f53b313421..ff98590057d4cf600e293b8dc902269543aae851 100644 (file)
@@ -8467,6 +8467,10 @@ def err_module_interface_implementation_mismatch : Error<
   "found while %select{not |not |}0building module interface">;
 def err_current_module_name_mismatch : Error<
   "module name '%0' specified on command line does not match name of module">;
+def err_module_redefinition : Error<
+  "redefinition of module '%0'">;
+def note_prev_module_definition : Note<"previously defined here">;
+def note_prev_module_definition_from_ast_file : Note<"module loaded from '%0'">;
 def err_module_private_specialization : Error<
   "%select{template|partial|member}0 specialization cannot be "
   "declared __module_private__">;
index 764e9bcb8987d3b197e5056fb0aa42147a1b23f4..b738f837178892feb8a2fbcd159ebc8db65087e1 100644 (file)
@@ -142,8 +142,9 @@ BENIGN_LANGOPT(EmitAllDecls      , 1, 0, "emitting all declarations")
 LANGOPT(MathErrno         , 1, 1, "errno in math functions")
 BENIGN_LANGOPT(HeinousExtensions , 1, 0, "extensions that we really don't like and may be ripped out at any time")
 LANGOPT(Modules           , 1, 0, "modules extension to C")
-LANGOPT(ModulesTS         , 1, 0, "C++ Modules TS")
-BENIGN_LANGOPT(CompilingModule, 1, 0, "compiling a module interface")
+COMPATIBLE_LANGOPT(ModulesTS  , 1, 0, "C++ Modules TS")
+BENIGN_ENUM_LANGOPT(CompilingModule, CompilingModuleKind, 2, CMK_None,
+                    "compiling a module interface")
 COMPATIBLE_LANGOPT(ModulesDeclUse    , 1, 0, "require declaration of module uses")
 BENIGN_LANGOPT(ModulesSearchAll  , 1, 1, "searching even non-imported modules to find unresolved references")
 COMPATIBLE_LANGOPT(ModulesStrictDeclUse, 1, 0, "requiring declaration of module uses and all headers to be in modules")
index 6ec499f1c74aa5104731e9714f1e7db0d9fb011d..5be78fecc7df287c3c1fb72b7978883afa98d976 100644 (file)
@@ -58,6 +58,12 @@ public:
     SOB_Trapping    // -ftrapv
   };
 
+  enum CompilingModuleKind {
+    CMK_None,           ///< Not compiling a module interface at all.
+    CMK_ModuleMap,      ///< Compiling a module from a module map.
+    CMK_ModuleInterface ///< Compiling a C++ modules TS module interface unit.
+  };
+
   enum PragmaMSPointersToMembersKind {
     PPTMK_BestCase,
     PPTMK_FullGeneralitySingleInheritance,
@@ -134,7 +140,12 @@ public:
   Type get##Name() const { return static_cast<Type>(Name); } \
   void set##Name(Type Value) { Name = static_cast<unsigned>(Value); }  
 #include "clang/Basic/LangOptions.def"
-  
+
+  /// Are we compiling a module interface (.cppm or module map)?
+  bool isCompilingModule() const {
+    return getCompilingModule() != CMK_None;
+  }
+
   bool isSignedOverflowDefined() const {
     return getSignedOverflowBehavior() == SOB_Defined;
   }
index f2cfc11c9cec73a4927ed31b4c3f620bb3800724..9eaf0c026105f2a01602dc548f7961a5b8d1ed65 100644 (file)
@@ -459,6 +459,8 @@ def print_decl_contexts : Flag<["-"], "print-decl-contexts">,
   HelpText<"Print DeclContexts and their Decls">;
 def emit_module : Flag<["-"], "emit-module">,
   HelpText<"Generate pre-compiled module file from a module map">;
+def emit_module_interface : Flag<["-"], "emit-module-interface">,
+  HelpText<"Generate pre-compiled module file from a C++ module interface">;
 def emit_pth : Flag<["-"], "emit-pth">,
   HelpText<"Generate pre-tokenized header file">;
 def emit_pch : Flag<["-"], "emit-pch">,
index b56a04ae0654d3385fb400c12d54fb581f66a7eb..a073ca5bfd2a5238d280a1c4f75d263ed3bb4876 100644 (file)
@@ -91,11 +91,12 @@ public:
 };
 
 class GenerateModuleAction : public ASTFrontendAction {
-  clang::Module *Module;
-  const FileEntry *ModuleMapForUniquing;
-  bool IsSystem;
-  
+  virtual std::unique_ptr<raw_pwrite_stream>
+  CreateOutputFile(CompilerInstance &CI, StringRef InFile) = 0;
+
 protected:
+  bool BeginSourceFileAction(CompilerInstance &CI, StringRef Filename) override;
+
   std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
                                                  StringRef InFile) override;
 
@@ -104,22 +105,31 @@ protected:
   }
 
   bool hasASTFileSupport() const override { return false; }
+};
+
+class GenerateModuleFromModuleMapAction : public GenerateModuleAction {
+  clang::Module *Module = nullptr;
+  const FileEntry *ModuleMapForUniquing = nullptr;
+  bool IsSystem = false;
+
+private:
+  bool BeginSourceFileAction(CompilerInstance &CI, StringRef Filename) override;
+
+  std::unique_ptr<raw_pwrite_stream>
+  CreateOutputFile(CompilerInstance &CI, StringRef InFile) override;
 
 public:
-  GenerateModuleAction(const FileEntry *ModuleMap = nullptr,
-                       bool IsSystem = false)
-    : ASTFrontendAction(), ModuleMapForUniquing(ModuleMap), IsSystem(IsSystem)
-  { }
+  GenerateModuleFromModuleMapAction() {}
+  GenerateModuleFromModuleMapAction(const FileEntry *ModuleMap, bool IsSystem)
+      : ModuleMapForUniquing(ModuleMap), IsSystem(IsSystem) {}
+};
 
+class GenerateModuleInterfaceAction : public GenerateModuleAction {
+private:
   bool BeginSourceFileAction(CompilerInstance &CI, StringRef Filename) override;
 
-  /// \brief Compute the AST consumer arguments that will be used to
-  /// create the PCHGenerator instance returned by CreateASTConsumer.
-  ///
-  /// \returns true if an error occurred, false otherwise.
   std::unique_ptr<raw_pwrite_stream>
-  ComputeASTConsumerArguments(CompilerInstance &CI, StringRef InFile,
-                              std::string &Sysroot, std::string &OutputFile);
+  CreateOutputFile(CompilerInstance &CI, StringRef InFile) override;
 };
 
 class SyntaxOnlyAction : public ASTFrontendAction {
index a75523f2564882778c9c378c0987ffd0b03c5ba3..4df5e2bb9ba178ef382a36674333a3ef1adb9463 100644 (file)
@@ -40,7 +40,9 @@ namespace frontend {
     EmitCodeGenOnly,        ///< Generate machine code, but don't emit anything.
     EmitObj,                ///< Emit a .o file.
     FixIt,                  ///< Parse and apply any fixits to the source.
-    GenerateModule,         ///< Generate pre-compiled module.
+    GenerateModule,         ///< Generate pre-compiled module from a module map.
+    GenerateModuleInterface,///< Generate pre-compiled module from a C++ module
+                            ///< interface file.
     GeneratePCH,            ///< Generate pre-compiled header.
     GeneratePTH,            ///< Generate pre-tokenized header.
     InitOnly,               ///< Only execute frontend initialization.
index 1e86f736983f6e8c0d179d0e0ea1be1a81ce71c5..9ab33abfef970a51d857de1921462ea474e7cdad 100644 (file)
@@ -402,6 +402,15 @@ public:
                                                bool IsFramework,
                                                bool IsExplicit);
 
+  /// \brief Create a new module for a C++ Modules TS module interface unit.
+  /// The module must not already exist, and will be configured for the current
+  /// compilation.
+  ///
+  /// Note that this also sets the current module to the newly-created module.
+  ///
+  /// \returns The newly-created module.
+  Module *createModuleForInterfaceUnit(SourceLocation Loc, StringRef Name);
+
   /// \brief Infer the contents of a framework module map from the given
   /// framework directory.
   Module *inferFrameworkModule(const DirectoryEntry *FrameworkDir,
index f66ff140aab939e3ed8893d7e23b6a9a38597bca..3acd64587b12b2ed1579ced350a78fe9631affa8 100644 (file)
@@ -2825,7 +2825,7 @@ const FileEntry *ASTUnit::getPCHFile() {
 }
 
 bool ASTUnit::isModuleFile() {
-  return isMainFileAST() && ASTFileLangOpts.CompilingModule;
+  return isMainFileAST() && ASTFileLangOpts.isCompilingModule();
 }
 
 void ASTUnit::PreambleData::countLines() const {
index 89907f1171938fec7c5019731d8894e47982f2d9..627134e8dd16d0460f25326228f3689533fd9276 100644 (file)
@@ -1026,7 +1026,7 @@ static bool compileModuleImpl(CompilerInstance &ImportingInstance,
 
   // Construct a module-generating action. Passing through the module map is
   // safe because the FileManager is shared between the compiler instances.
-  GenerateModuleAction CreateModuleAction(
+  GenerateModuleFromModuleMapAction CreateModuleAction(
       ModMap.getModuleMapFileForUniquing(Module), Module->IsSystem);
 
   ImportingInstance.getDiagnostics().Report(ImportLoc,
index 97c37defa1083f3cc2f3826628c171fc87b9a5d8..3154291d26fa6ad26575678b91d7fefcdfd22f96 100644 (file)
@@ -1116,6 +1116,8 @@ static InputKind ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args,
       Opts.ProgramAction = frontend::FixIt; break;
     case OPT_emit_module:
       Opts.ProgramAction = frontend::GenerateModule; break;
+    case OPT_emit_module_interface:
+      Opts.ProgramAction = frontend::GenerateModuleInterface; break;
     case OPT_emit_pch:
       Opts.ProgramAction = frontend::GeneratePCH; break;
     case OPT_emit_pth:
@@ -2259,6 +2261,7 @@ static void ParsePreprocessorOutputArgs(PreprocessorOutputOptions &Opts,
   case frontend::EmitObj:
   case frontend::FixIt:
   case frontend::GenerateModule:
+  case frontend::GenerateModuleInterface:
   case frontend::GeneratePCH:
   case frontend::GeneratePTH:
   case frontend::ParseSyntaxOnly:
index d73d46984cca13c4ab9aeba484b3f437e9cf4e28..eb91940cbbfc8078ad05eecf314ec8edbe62a144 100644 (file)
@@ -130,13 +130,13 @@ GeneratePCHAction::ComputeASTConsumerArguments(CompilerInstance &CI,
 std::unique_ptr<ASTConsumer>
 GenerateModuleAction::CreateASTConsumer(CompilerInstance &CI,
                                         StringRef InFile) {
-  std::string Sysroot;
-  std::string OutputFile;
-  std::unique_ptr<raw_pwrite_stream> OS =
-      ComputeASTConsumerArguments(CI, InFile, Sysroot, OutputFile);
+  std::unique_ptr<raw_pwrite_stream> OS = CreateOutputFile(CI, InFile);
   if (!OS)
     return nullptr;
 
+  std::string OutputFile = CI.getFrontendOpts().OutputFile;
+  std::string Sysroot;
+
   auto Buffer = std::make_shared<PCHBuffer>();
   std::vector<std::unique_ptr<ASTConsumer>> Consumers;
 
@@ -151,6 +151,23 @@ GenerateModuleAction::CreateASTConsumer(CompilerInstance &CI,
   return llvm::make_unique<MultiplexConsumer>(std::move(Consumers));
 }
 
+bool GenerateModuleAction::BeginSourceFileAction(CompilerInstance &CI, 
+                                                 StringRef Filename) {
+  // Set up embedding for any specified files. Do this before we load any
+  // source files, including the primary module map for the compilation.
+  for (const auto &F : CI.getFrontendOpts().ModulesEmbedFiles) {
+    if (const auto *FE = CI.getFileManager().getFile(F, /*openFile*/true))
+      CI.getSourceManager().setFileIsTransient(FE);
+    else
+      CI.getDiagnostics().Report(diag::err_modules_embed_file_not_found) << F;
+  }
+  if (CI.getFrontendOpts().ModulesEmbedAllFiles)
+    CI.getSourceManager().setAllFilesAreTransient(true);
+
+  return true;
+}
+
+
 static SmallVectorImpl<char> &
 operator+=(SmallVectorImpl<char> &Includes, StringRef RHS) {
   Includes.append(RHS.begin(), RHS.end());
@@ -266,9 +283,12 @@ collectModuleHeaderIncludes(const LangOptions &LangOpts, FileManager &FileMgr,
   return std::error_code();
 }
 
-bool GenerateModuleAction::BeginSourceFileAction(CompilerInstance &CI, 
-                                                 StringRef Filename) {
-  CI.getLangOpts().CompilingModule = true;
+bool GenerateModuleFromModuleMapAction::BeginSourceFileAction(
+    CompilerInstance &CI, StringRef Filename) {
+  CI.getLangOpts().setCompilingModule(LangOptions::CMK_ModuleMap);
+
+  if (!GenerateModuleAction::BeginSourceFileAction(CI, Filename))
+    return false;
 
   // Find the module map file.
   const FileEntry *ModuleMap =
@@ -279,17 +299,6 @@ bool GenerateModuleAction::BeginSourceFileAction(CompilerInstance &CI,
     return false;
   }
   
-  // Set up embedding for any specified files. Do this before we load any
-  // source files, including the primary module map for the compilation.
-  for (const auto &F : CI.getFrontendOpts().ModulesEmbedFiles) {
-    if (const auto *FE = CI.getFileManager().getFile(F, /*openFile*/true))
-      CI.getSourceManager().setFileIsTransient(FE);
-    else
-      CI.getDiagnostics().Report(diag::err_modules_embed_file_not_found) << F;
-  }
-  if (CI.getFrontendOpts().ModulesEmbedAllFiles)
-    CI.getSourceManager().setAllFilesAreTransient(true);
-
   // Parse the module map file.
   HeaderSearch &HS = CI.getPreprocessor().getHeaderSearchInfo();
   if (HS.loadModuleMapFile(ModuleMap, IsSystem))
@@ -381,10 +390,8 @@ bool GenerateModuleAction::BeginSourceFileAction(CompilerInstance &CI,
 }
 
 std::unique_ptr<raw_pwrite_stream>
-GenerateModuleAction::ComputeASTConsumerArguments(CompilerInstance &CI,
-                                                  StringRef InFile,
-                                                  std::string &Sysroot,
-                                                  std::string &OutputFile) {
+GenerateModuleFromModuleMapAction::CreateOutputFile(CompilerInstance &CI,
+                                                    StringRef InFile) {
   // If no output file was provided, figure out where this module would go
   // in the module cache.
   if (CI.getFrontendOpts().OutputFile.empty()) {
@@ -398,16 +405,28 @@ GenerateModuleAction::ComputeASTConsumerArguments(CompilerInstance &CI,
   // We use createOutputFile here because this is exposed via libclang, and we
   // must disable the RemoveFileOnSignal behavior.
   // We use a temporary to avoid race conditions.
-  std::unique_ptr<raw_pwrite_stream> OS =
-      CI.createOutputFile(CI.getFrontendOpts().OutputFile, /*Binary=*/true,
-                          /*RemoveFileOnSignal=*/false, InFile,
-                          /*Extension=*/"", /*useTemporary=*/true,
-                          /*CreateMissingDirectories=*/true);
-  if (!OS)
-    return nullptr;
+  return CI.createOutputFile(CI.getFrontendOpts().OutputFile, /*Binary=*/true,
+                             /*RemoveFileOnSignal=*/false, InFile,
+                             /*Extension=*/"", /*useTemporary=*/true,
+                             /*CreateMissingDirectories=*/true);
+}
 
-  OutputFile = CI.getFrontendOpts().OutputFile;
-  return OS;
+bool GenerateModuleInterfaceAction::BeginSourceFileAction(CompilerInstance &CI,
+                                                          StringRef Filename) {
+  if (!CI.getLangOpts().ModulesTS) {
+    CI.getDiagnostics().Report(diag::err_module_interface_requires_modules_ts);
+    return false;
+  }
+
+  CI.getLangOpts().setCompilingModule(LangOptions::CMK_ModuleInterface);
+
+  return GenerateModuleAction::BeginSourceFileAction(CI, Filename);
+}
+
+std::unique_ptr<raw_pwrite_stream>
+GenerateModuleInterfaceAction::CreateOutputFile(CompilerInstance &CI,
+                                                StringRef InFile) {
+  return CI.createDefaultOutputFile(/*Binary=*/true, InFile, "pcm");
 }
 
 SyntaxOnlyAction::~SyntaxOnlyAction() {
index 9ede674e47ea60cf0cb739735fe2751fbc7aeb87..6a82084aff1bed574e021ac29c97727e08412375 100644 (file)
@@ -25,6 +25,8 @@ InputKind FrontendOptions::getInputKindForExtension(StringRef Extension) {
     .Case("mii", IK_PreprocessedObjCXX)
     .Cases("C", "cc", "cp", IK_CXX)
     .Cases("cpp", "CPP", "c++", "cxx", "hpp", IK_CXX)
+    .Case("cppm", IK_CXX)
+    .Case("iim", IK_PreprocessedCXX)
     .Case("cl", IK_OpenCL)
     .Case("cu", IK_CUDA)
     .Cases("ll", "bc", IK_LLVM_IR)
index 13cb52aa1e3fce8b8e15fd3518b7485683fdbcf2..187a6e76245d4f055d5011e79e8e5c398e99ed09 100644 (file)
@@ -52,7 +52,10 @@ CreateFrontendBaseAction(CompilerInstance &CI) {
   case EmitCodeGenOnly:        return llvm::make_unique<EmitCodeGenOnlyAction>();
   case EmitObj:                return llvm::make_unique<EmitObjAction>();
   case FixIt:                  return llvm::make_unique<FixItAction>();
-  case GenerateModule:         return llvm::make_unique<GenerateModuleAction>();
+  case GenerateModule:
+    return llvm::make_unique<GenerateModuleFromModuleMapAction>();
+  case GenerateModuleInterface:
+    return llvm::make_unique<GenerateModuleInterfaceAction>();
   case GeneratePCH:            return llvm::make_unique<GeneratePCHAction>();
   case GeneratePTH:            return llvm::make_unique<GeneratePTHAction>();
   case InitOnly:               return llvm::make_unique<InitOnlyAction>();
index 3e3215dee82a88d07a09a3f0a57b30aa3bc0c692..50eb6f82c275eb59f447c565eeb18d6bb251efc1 100644 (file)
@@ -558,6 +558,25 @@ ModuleMap::findOrCreateModule(StringRef Name, Module *Parent, bool IsFramework,
   return std::make_pair(Result, true);
 }
 
+Module *ModuleMap::createModuleForInterfaceUnit(SourceLocation Loc,
+                                                StringRef Name) {
+  assert(LangOpts.CurrentModule == Name && "module name mismatch");
+  assert(!Modules[Name] && "redefining existing module");
+
+  auto *Result =
+      new Module(Name, Loc, nullptr, /*IsFramework*/ false,
+                 /*IsExplicit*/ false, NumCreatedModules++);
+  Modules[Name] = SourceModule = Result;
+
+  // Mark the main source file as being within the newly-created module so that
+  // declarations and macros are properly visibility-restricted to it.
+  auto *MainFile = SourceMgr.getFileEntryForID(SourceMgr.getMainFileID());
+  assert(MainFile && "no input file for module interface");
+  Headers[MainFile].push_back(KnownHeader(Result, PrivateHeader));
+
+  return Result;
+}
+
 /// \brief For a framework module, infer the framework against which we
 /// should link.
 static void inferFrameworkLink(Module *Mod, const DirectoryEntry *FrameworkDir,
@@ -805,7 +824,7 @@ void ModuleMap::addHeader(Module *Mod, Module::Header Header,
   Mod->Headers[headerRoleToKind(Role)].push_back(std::move(Header));
 
   bool isCompilingModuleHeader =
-      LangOpts.CompilingModule && Mod->getTopLevelModule() == SourceModule;
+      LangOpts.isCompilingModule() && Mod->getTopLevelModule() == SourceModule;
   if (!Imported || isCompilingModuleHeader) {
     // When we import HeaderFileInfo, the external source is expected to
     // set the isModuleHeader flag itself.
index e2eceafd983b86cfcc8691f2d37bc6b450c0eefa..33d690de8b257b89a986c0f91a6da101b76b9130 100644 (file)
@@ -685,7 +685,7 @@ bool Preprocessor::needModuleMacros() const {
     return true;
   // Otherwise, we only need module macros if we're actually compiling a module
   // interface.
-  return getLangOpts().CompilingModule;
+  return getLangOpts().isCompilingModule();
 }
 
 void Preprocessor::LeaveSubmodule() {
index a201838e815236efc0c2dd1acf24d565236ab3ee..3566c3429fea4011f9ef3473885dbb359306ce89 100644 (file)
@@ -1795,7 +1795,7 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) {
       [this](Token &Tok, bool &HasLexedNextToken) -> int {
         IdentifierInfo *II = ExpectFeatureIdentifierInfo(Tok, *this,
                                        diag::err_expected_id_building_module);
-        return getLangOpts().CompilingModule && II &&
+        return getLangOpts().isCompilingModule() && II &&
                (II->getName() == getLangOpts().CurrentModule);
       });
   } else if (II == Ident__MODULE__) {
index 096a6107a9513aa322dc0e75be36a44e67c88fdc..1f28b3b268d50431f12484b35065cfdfc346bcd9 100644 (file)
@@ -480,7 +480,7 @@ void Preprocessor::CreateString(StringRef Str, Token &Tok,
 }
 
 Module *Preprocessor::getCurrentModule() {
-  if (!getLangOpts().CompilingModule)
+  if (!getLangOpts().isCompilingModule())
     return nullptr;
 
   return getHeaderSearchInfo().lookupModule(getLangOpts().CurrentModule);
@@ -795,6 +795,23 @@ void Preprocessor::LexAfterModuleImport(Token &Result) {
 
   // If we have a non-empty module path, load the named module.
   if (!ModuleImportPath.empty()) {
+    // Under the Modules TS, the dot is just part of the module name, and not
+    // a real hierarachy separator. Flatten such module names now.
+    //
+    // FIXME: Is this the right level to be performing this transformation?
+    std::string FlatModuleName;
+    if (getLangOpts().ModulesTS) {
+      for (auto &Piece : ModuleImportPath) {
+        if (!FlatModuleName.empty())
+          FlatModuleName += ".";
+        FlatModuleName += Piece.first->getName();
+      }
+      SourceLocation FirstPathLoc = ModuleImportPath[0].second;
+      ModuleImportPath.clear();
+      ModuleImportPath.push_back(
+          std::make_pair(getIdentifierInfo(FlatModuleName), FirstPathLoc));
+    }
+
     Module *Imported = nullptr;
     if (getLangOpts().Modules) {
       Imported = TheModuleLoader.loadModule(ModuleImportLoc,
index bab3dbefb0d5a9ab4b81c32d290c672e2511f28c..d018d4c08ed967ef761e8370aa7e1df2bea66a3f 100644 (file)
@@ -138,26 +138,18 @@ void clang::ParseAST(Sema &S, bool PrintStats, bool SkipFunctionBodies) {
   S.getPreprocessor().EnterMainSourceFile();
   P.Initialize();
 
-  // C11 6.9p1 says translation units must have at least one top-level
-  // declaration. C++ doesn't have this restriction. We also don't want to
-  // complain if we have a precompiled header, although technically if the PCH
-  // is empty we should still emit the (pedantic) diagnostic.
   Parser::DeclGroupPtrTy ADecl;
   ExternalASTSource *External = S.getASTContext().getExternalSource();
   if (External)
     External->StartTranslationUnit(Consumer);
 
-  if (P.ParseFirstTopLevelDecl(ADecl)) {
-    if (!External && !S.getLangOpts().CPlusPlus)
-      P.Diag(diag::ext_empty_translation_unit);
-  } else {
-    do {
-      // If we got a null return and something *was* parsed, ignore it.  This
-      // is due to a top-level semicolon, an action override, or a parse error
-      // skipping something.
-      if (ADecl && !Consumer->HandleTopLevelDecl(ADecl.get()))
-        return;
-    } while (!P.ParseTopLevelDecl(ADecl));
+  for (bool AtEOF = P.ParseFirstTopLevelDecl(ADecl); !AtEOF;
+       AtEOF = P.ParseTopLevelDecl(ADecl)) {
+    // If we got a null return and something *was* parsed, ignore it.  This
+    // is due to a top-level semicolon, an action override, or a parse error
+    // skipping something.
+    if (ADecl && !Consumer->HandleTopLevelDecl(ADecl.get()))
+      return;
   }
 
   // Process any TopLevelDecls generated by #pragma weak.
index a391363ff38375dfe520da7abfb5ac804fc82c41..e58e3f7c20ed0a67018f43e1b8ebac0f0972ddc0 100644 (file)
@@ -544,11 +544,21 @@ bool Parser::ParseFirstTopLevelDecl(DeclGroupPtrTy &Result) {
   if (Tok.is(tok::kw_module)) {
     Result = ParseModuleDecl();
     return false;
+  } else if (getLangOpts().getCompilingModule() ==
+             LangOptions::CMK_ModuleInterface) {
+    Diag(Tok, diag::err_expected_module_interface_decl);
   }
-  // FIXME: If we're parsing a module interface and we don't have a module
-  // declaration here, diagnose.
 
-  return ParseTopLevelDecl(Result);
+  // C11 6.9p1 says translation units must have at least one top-level
+  // declaration. C++ doesn't have this restriction. We also don't want to
+  // complain if we have a precompiled header, although technically if the PCH
+  // is empty we should still emit the (pedantic) diagnostic.
+  bool NoTopLevelDecls = ParseTopLevelDecl(Result);
+  if (NoTopLevelDecls && !Actions.getASTContext().getExternalSource() &&
+      !getLangOpts().CPlusPlus)
+    Diag(diag::ext_empty_translation_unit);
+
+  return NoTopLevelDecls;
 }
 
 /// ParseTopLevelDecl - Parse one top-level declaration, return whatever the
@@ -820,6 +830,11 @@ Parser::ParseExternalDeclaration(ParsedAttributesWithRange &attrs,
     ParseMicrosoftIfExistsExternalDeclaration();
     return nullptr;
 
+  case tok::kw_module:
+    Diag(Tok, diag::err_unexpected_module_decl);
+    SkipUntil(tok::semi);
+    return nullptr;
+
   default:
   dont_know:
     // We can't tell whether this is a function-definition or declaration yet.
index 9122030e16d7634dd2b623ed7c170f12ae0dc8bf..c756d1f6ab6ea47703ee920d1f86e49b9528a61f 100644 (file)
@@ -15180,48 +15180,84 @@ void Sema::diagnoseMisplacedModuleImport(Module *M, SourceLocation ImportLoc) {
 Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation ModuleLoc,
                                            ModuleDeclKind MDK,
                                            ModuleIdPath Path) {
-  // We should see 'module implementation' if and only if we are not compiling
-  // a module interface.
-  if (getLangOpts().CompilingModule ==
-      (MDK == ModuleDeclKind::Implementation)) {
+  // 'module implementation' requires that we are not compiling a module of any
+  // kind. 'module' and 'module partition' require that we are compiling a
+  // module inteface (not a module map).
+  auto CMK = getLangOpts().getCompilingModule();
+  if (MDK == ModuleDeclKind::Implementation
+          ? CMK != LangOptions::CMK_None
+          : CMK != LangOptions::CMK_ModuleInterface) {
     Diag(ModuleLoc, diag::err_module_interface_implementation_mismatch)
       << (unsigned)MDK;
     return nullptr;
   }
 
   // FIXME: Create a ModuleDecl and return it.
-  // FIXME: Teach the lexer to handle this declaration too.
+
+  // FIXME: Most of this work should be done by the preprocessor rather than
+  // here, in case we look ahead across something where the current
+  // module matters (eg a #include).
+
+  // The dots in a module name in the Modules TS are a lie. Unlike Clang's
+  // hierarchical module map modules, the dots here are just another character
+  // that can appear in a module name. Flatten down to the actual module name.
+  std::string ModuleName;
+  for (auto &Piece : Path) {
+    if (!ModuleName.empty())
+      ModuleName += ".";
+    ModuleName += Piece.first->getName();
+  }
+
+  // If a module name was explicitly specified on the command line, it must be
+  // correct.
+  if (!getLangOpts().CurrentModule.empty() &&
+      getLangOpts().CurrentModule != ModuleName) {
+    Diag(Path.front().second, diag::err_current_module_name_mismatch)
+        << SourceRange(Path.front().second, Path.back().second)
+        << getLangOpts().CurrentModule;
+    return nullptr;
+  }
+  const_cast<LangOptions&>(getLangOpts()).CurrentModule = ModuleName;
+
+  auto &Map = PP.getHeaderSearchInfo().getModuleMap();
 
   switch (MDK) {
-  case ModuleDeclKind::Module:
+  case ModuleDeclKind::Module: {
     // FIXME: Check we're not in a submodule.
-    // FIXME: Set CurrentModule and create a corresponding Module object.
+
+    // We can't have imported a definition of this module or parsed a module
+    // map defining it already.
+    if (auto *M = Map.findModule(ModuleName)) {
+      Diag(Path[0].second, diag::err_module_redefinition) << ModuleName;
+      if (M->DefinitionLoc.isValid())
+        Diag(M->DefinitionLoc, diag::note_prev_module_definition);
+      else if (const auto *FE = M->getASTFile())
+        Diag(M->DefinitionLoc, diag::note_prev_module_definition_from_ast_file)
+            << FE->getName();
+      return nullptr;
+    }
+
+    // Create a Module for the module that we're defining.
+    Module *Mod = Map.createModuleForInterfaceUnit(ModuleLoc, ModuleName);
+    assert(Mod && "module creation should not fail");
+
+    // Enter the semantic scope of the module.
+    ActOnModuleBegin(ModuleLoc, Mod);
     return nullptr;
+  }
 
   case ModuleDeclKind::Partition:
     // FIXME: Check we are in a submodule of the named module.
     return nullptr;
 
   case ModuleDeclKind::Implementation:
-    DeclResult Import = ActOnModuleImport(ModuleLoc, ModuleLoc, Path);
+    std::pair<IdentifierInfo *, SourceLocation> ModuleNameLoc(
+        PP.getIdentifierInfo(ModuleName), Path[0].second);
+
+    DeclResult Import = ActOnModuleImport(ModuleLoc, ModuleLoc, ModuleNameLoc);
     if (Import.isInvalid())
       return nullptr;
-    ImportDecl *ID = cast<ImportDecl>(Import.get());
-
-    // The current module is whatever we just loaded.
-    //
-    // FIXME: We should probably do this from the lexer rather than waiting
-    // until now, in case we look ahead across something where the current
-    // module matters (eg a #include).
-    auto Name = ID->getImportedModule()->getTopLevelModuleName();
-    if (!getLangOpts().CurrentModule.empty() &&
-        getLangOpts().CurrentModule != Name) {
-      Diag(Path.front().second, diag::err_current_module_name_mismatch)
-          << SourceRange(Path.front().second, Path.back().second)
-          << getLangOpts().CurrentModule;
-    }
-    const_cast<LangOptions&>(getLangOpts()).CurrentModule = Name;
-    return ConvertDeclToDeclGroup(ID);
+    return ConvertDeclToDeclGroup(Import.get());
   }
 
   llvm_unreachable("unexpected module decl kind");
@@ -15246,8 +15282,8 @@ DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc,
   // Import-from-implementation is valid in the Modules TS. FIXME: Should we
   // warn on a redundant import of the current module?
   if (Mod->getTopLevelModuleName() == getLangOpts().CurrentModule &&
-      (getLangOpts().CompilingModule || !getLangOpts().ModulesTS))
-    Diag(ImportLoc, getLangOpts().CompilingModule
+      (getLangOpts().isCompilingModule() || !getLangOpts().ModulesTS))
+    Diag(ImportLoc, getLangOpts().isCompilingModule()
                         ? diag::err_module_self_import
                         : diag::err_module_import_in_implementation)
         << Mod->getFullModuleName() << getLangOpts().CurrentModule;
index 9750d53743b1221ac5a02bbccc62ab93e3198099..3237cd005215008c6ca6d57d1525be270b353558 100644 (file)
@@ -3217,7 +3217,7 @@ void Sema::addMethodToGlobalList(ObjCMethodList *List,
   ObjCMethodList *ListWithSameDeclaration = nullptr;
   for (; List; Previous = List, List = List->getNext()) {
     // If we are building a module, keep all of the methods.
-    if (getLangOpts().CompilingModule)
+    if (getLangOpts().isCompilingModule())
       continue;
 
     bool SameDeclaration = MatchTwoMethodDeclarations(Method,
index b2d79a20871d47e5d055e5c1661b58af3e3d50fa..e1765dafd96fb8e568cf8058d3f0456615ee4a82 100644 (file)
@@ -46,10 +46,13 @@ void PCHGenerator::HandleTranslationUnit(ASTContext &Ctx) {
     return;
 
   Module *Module = nullptr;
-  if (PP.getLangOpts().CompilingModule) {
+  if (PP.getLangOpts().isCompilingModule()) {
     Module = PP.getHeaderSearchInfo().lookupModule(
         PP.getLangOpts().CurrentModule, /*AllowSearch*/ false);
-    assert(Module && "emitting module but current module doesn't exist");
+    if (!Module) {
+      assert(hasErrors && "emitting module but current module doesn't exist");
+      return;
+    }
   }
 
   // Emit the PCH file to the Buffer.
index cd4174eabd05aed7933903dd31d4f71a22f6dae7..0600eddecb834ae94e76f65debf922d8f43c46d7 100644 (file)
@@ -1,22 +1,26 @@
 // RUN: rm -rf %t
 // RUN: mkdir -p %t
-// RUN: echo 'int a, b;' > %t/x.h
-// RUN: echo 'module x { header "x.h" module y {} } module z {}' > %t/map
-// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%S/Inputs -fmodules-cache-path=%t -fmodule-map-file=%t/map -verify %s \
+// RUN: echo 'module x; int a, b;' > %t/x.cppm
+// RUN: echo 'module x.y; int c;' > %t/x.y.cppm
+//
+// RUN: %clang_cc1 -std=c++1z -fmodules-ts -emit-module-interface %t/x.cppm -o %t/x.pcm
+// RUN: %clang_cc1 -std=c++1z -fmodules-ts -emit-module-interface -fmodule-file=%t/x.pcm %t/x.y.cppm -o %t/x.y.pcm
+//
+// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=%t/x.y.pcm -verify %s \
 // RUN:            -DTEST=1 -DMODULE_KIND=implementation -DMODULE_NAME=z
-// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%S/Inputs -fmodules-cache-path=%t -fmodule-map-file=%t/map -verify %s \
+// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=%t/x.y.pcm -verify %s \
 // RUN:            -DTEST=2 -DMODULE_KIND=implementation -DMODULE_NAME=x
-// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%S/Inputs -fmodules-cache-path=%t -fmodule-map-file=%t/map -verify %s \
+// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=%t/x.y.pcm -verify %s \
 // RUN:            -DTEST=3 -DMODULE_KIND= -DMODULE_NAME=z
-// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%S/Inputs -fmodules-cache-path=%t -fmodule-map-file=%t/map -verify %s \
+// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=%t/x.y.pcm -verify %s \
 // RUN:            -DTEST=4 -DMODULE_KIND=partition -DMODULE_NAME=z
-// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%S/Inputs -fmodules-cache-path=%t -fmodule-map-file=%t/map -verify %s \
+// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=%t/x.y.pcm -verify %s \
 // RUN:            -DTEST=5 -DMODULE_KIND=elderberry -DMODULE_NAME=z
-// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%S/Inputs -fmodules-cache-path=%t -fmodule-map-file=%t/map -verify %s \
+// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=%t/x.y.pcm -verify %s \
 // RUN:            -DTEST=1 -DMODULE_KIND=implementation -DMODULE_NAME='z [[]]'
-// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%S/Inputs -fmodules-cache-path=%t -fmodule-map-file=%t/map -verify %s \
+// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=%t/x.y.pcm -verify %s \
 // RUN:            -DTEST=6 -DMODULE_KIND=implementation -DMODULE_NAME='z [[fancy]]'
-// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%S/Inputs -fmodules-cache-path=%t -fmodule-map-file=%t/map -verify %s \
+// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=%t/x.y.pcm -verify %s \
 // RUN:            -DTEST=7 -DMODULE_KIND=implementation -DMODULE_NAME='z [[maybe_unused]]'
 
 module MODULE_KIND MODULE_NAME;
@@ -34,7 +38,8 @@ module MODULE_KIND MODULE_NAME;
 
 int use_1 = a;
 #if TEST != 2
-// expected-error@-2 {{undeclared}}
+// expected-error@-2 {{declaration of 'a' must be imported from module 'x' before it is required}}
+// expected-note@x.cppm:1 {{here}}
 #endif
 
 import x;
diff --git a/test/Parser/cxx-modules-interface.cppm b/test/Parser/cxx-modules-interface.cppm
new file mode 100644 (file)
index 0000000..8874771
--- /dev/null
@@ -0,0 +1,21 @@
+// RUN:     %clang_cc1 -std=c++1z -fmodules-ts -emit-module-interface %s -o %t.pcm -verify -DTEST=0
+// RUN:     %clang_cc1 -std=c++1z -fmodules-ts -emit-module-interface %s -o %t.pcm -verify -Dmodule=int -DTEST=1
+// RUN: not %clang_cc1 -std=c++1z -fmodules-ts -emit-module-interface %s -fmodule-file=%t.pcm -o %t.pcm -DTEST=2 2>&1 | FileCheck %s --check-prefix=CHECK-2
+// RUN:     %clang_cc1 -std=c++1z -fmodules-ts -emit-module-interface %s -fmodule-file=%t.pcm -o %t.pcm -verify -Dfoo=bar -DTEST=3
+
+#if TEST == 0
+// expected-no-diagnostics
+#endif
+
+module foo;
+#if TEST == 1
+// expected-error@-2 {{expected module declaration at start of module interface}}
+#elif TEST == 2
+// CHECK-2: error: redefinition of module 'foo'
+#endif
+
+int n;
+#if TEST == 3
+// expected-error@-2 {{redefinition of 'n'}}
+// expected-note@-3 {{previous}}
+#endif
index e7ce8fac7c1b13ec201b16b06907a7dc901ae8b1..497058b58beae047ede37d49163d8b8c45596566 100644 (file)
@@ -44,7 +44,7 @@ else:
 config.test_format = lit.formats.ShTest(execute_external)
 
 # suffixes: A list of file extensions to treat as test files.
-config.suffixes = ['.c', '.cpp', '.m', '.mm', '.cu', '.ll', '.cl', '.s', '.S', '.modulemap', '.test', '.rs']
+config.suffixes = ['.c', '.cpp', '.cppm', '.m', '.mm', '.cu', '.ll', '.cl', '.s', '.S', '.modulemap', '.test', '.rs']
 
 # excludes: A list of directories to exclude from the testsuite. The 'Inputs'
 # subdirectories contain auxiliary inputs for various tests in their parent