iains updated this revision to Diff 418041.
iains added a comment.

rebased, renamed helper method.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D121095/new/

https://reviews.llvm.org/D121095

Files:
  clang/include/clang/Basic/LangOptions.def
  clang/include/clang/Basic/LangOptions.h
  clang/include/clang/Basic/Module.h
  clang/include/clang/Driver/Options.td
  clang/include/clang/Frontend/FrontendActions.h
  clang/include/clang/Frontend/FrontendOptions.h
  clang/include/clang/Lex/ModuleMap.h
  clang/include/clang/Sema/Sema.h
  clang/lib/AST/Decl.cpp
  clang/lib/Frontend/CompilerInvocation.cpp
  clang/lib/Frontend/FrontendActions.cpp
  clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
  clang/lib/Lex/ModuleMap.cpp
  clang/lib/Parse/Parser.cpp
  clang/lib/Sema/Sema.cpp
  clang/lib/Sema/SemaModule.cpp
  clang/test/Modules/cxx20-hu-01.cpp

Index: clang/test/Modules/cxx20-hu-01.cpp
===================================================================
--- /dev/null
+++ clang/test/Modules/cxx20-hu-01.cpp
@@ -0,0 +1,104 @@
+// Test generation and import of simple C++20 Header Units.
+
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+// RUN: split-file %s %t
+
+// RUN: %clang_cc1 -std=c++20 -emit-header-unit -xc++-header %t/hu-01.h \
+// RUN:  -o %t/hu-01.pcm
+
+// RUN: %clang_cc1 -std=c++20 -module-file-info %t/hu-01.pcm | \
+// RUN: FileCheck --check-prefix=CHECK-HU %s -DTDIR=%t
+
+// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/imp-hu-01.cpp \
+// RUN:  -fmodule-file=%t/hu-01.pcm -o %t/B.pcm -Rmodule-import 2>&1  | \
+// RUN: FileCheck --check-prefix=CHECK-IMP %s -DTDIR=%t
+
+// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/imp-hu-02.cpp \
+// RUN:  -fmodule-file=%t/hu-01.pcm -o %t/C.pcm -Rmodule-import 2>&1  | \
+// RUN: FileCheck --check-prefix=CHECK-GMF-IMP %s -DTDIR=%t
+
+// RUN: %clang_cc1 -std=c++20 -emit-header-unit -xc++-header %t/hu-02.h \
+// RUN:  -o %t/hu-02.pcm
+
+// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/imp-hu-03.cpp \
+// RUN:  -fmodule-file=%t/hu-01.pcm -fmodule-file=%t/hu-02.pcm -o %t/D.pcm \
+// RUN: -Rmodule-import 2>&1 | \
+// RUN: FileCheck --check-prefix=CHECK-BOTH %s -DTDIR=%t
+
+// RUN: %clang_cc1 -std=c++20 -emit-header-unit -xc++-header %t/hu-03.h \
+// RUN: -fmodule-file=%t/hu-01.pcm  -o %t/hu-03.pcm
+
+// RUN: %clang_cc1 -std=c++20 -module-file-info %t/hu-03.pcm | \
+// RUN: FileCheck --check-prefix=CHECK-HU-HU %s -DTDIR=%t
+
+// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/imp-hu-04.cpp \
+// RUN:  -fmodule-file=%t/hu-03.pcm -o %t/E.pcm -Rmodule-import 2>&1 | \
+// RUN: FileCheck --check-prefix=CHECK-NESTED %s -DTDIR=%t
+
+//--- hu-01.h
+int foo(int);
+
+// CHECK-HU:  ====== C++20 Module structure ======
+// CHECK-HU-NEXT:  Header Unit '[[TDIR]]/hu-01.h' is the Primary Module at index #1
+
+//--- imp-hu-01.cpp
+export module B;
+import "hu-01.h";
+
+int bar(int x) {
+  return foo(x);
+}
+// CHECK-IMP: remark: importing module '[[TDIR]]/hu-01.h' from '[[TDIR]]/hu-01.pcm'
+// expected-no-diagnostics
+
+//--- imp-hu-02.cpp
+module;
+import "hu-01.h";
+
+export module C;
+
+int bar(int x) {
+  return foo(x);
+}
+// CHECK-GMF-IMP: remark: importing module '[[TDIR]]/hu-01.h' from '[[TDIR]]/hu-01.pcm'
+// expected-no-diagnostics
+
+//--- hu-02.h
+int baz(int);
+
+//--- imp-hu-03.cpp
+module;
+export import "hu-01.h";
+
+export module D;
+import "hu-02.h";
+
+int bar(int x) {
+  return foo(x) + baz(x);
+}
+// CHECK-BOTH: remark: importing module '[[TDIR]]/hu-01.h' from '[[TDIR]]/hu-01.pcm'
+// CHECK-BOTH: remark: importing module '[[TDIR]]/hu-02.h' from '[[TDIR]]/hu-02.pcm'
+// expected-no-diagnostics
+
+//--- hu-03.h
+export import "hu-01.h";
+int baz(int);
+// CHECK-HU-HU:  ====== C++20 Module structure ======
+// CHECK-HU-HU-NEXT:  Header Unit '[[TDIR]]/hu-03.h' is the Primary Module at index #2
+// CHECK-HU-HU-NEXT:   Exports:
+// CHECK-HU-HU-NEXT:    Header Unit '[[TDIR]]/hu-01.h' is at index #1
+
+// expected-no-diagnostics
+
+//--- imp-hu-04.cpp
+module;
+import "hu-03.h";
+
+export module E;
+
+int bar(int x) {
+  return foo(x) + baz(x);
+}
+// CHECK-NESTED: remark: importing module '[[TDIR]]/hu-03.h' from '[[TDIR]]/hu-03.pcm'
+// expected-no-diagnostics
Index: clang/lib/Sema/SemaModule.cpp
===================================================================
--- clang/lib/Sema/SemaModule.cpp
+++ clang/lib/Sema/SemaModule.cpp
@@ -97,6 +97,38 @@
   return nullptr;
 }
 
+void Sema::HandleStartOfHeaderUnit() {
+  assert(getLangOpts().CPlusPlusModules &&
+         "Header units are only valid for C++20 modules");
+  SourceLocation StartOfTU =
+      SourceMgr.getLocForStartOfFile(SourceMgr.getMainFileID());
+
+  StringRef HUName = getLangOpts().CurrentModule;
+  if (HUName.empty()) {
+    HUName = SourceMgr.getFileEntryForID(SourceMgr.getMainFileID())->getName();
+    const_cast<LangOptions &>(getLangOpts()).CurrentModule = HUName.str();
+  }
+
+  auto &Map = PP.getHeaderSearchInfo().getModuleMap();
+  // TODO: Make the C++20 header lookup independent.
+  Module::Header H{getLangOpts().CurrentModule, getLangOpts().CurrentModule,
+                   SourceMgr.getFileEntryForID(SourceMgr.getMainFileID())};
+  Module *Mod = Map.createHeaderUnit(StartOfTU, HUName, H);
+  assert(Mod && "module creation should not fail");
+  ModuleScopes.push_back({}); // No GMF
+  ModuleScopes.back().BeginLoc = StartOfTU;
+  ModuleScopes.back().Module = Mod;
+  ModuleScopes.back().ModuleInterface = true;
+  ModuleScopes.back().IsPartition = false;
+  VisibleModules.setVisible(Mod, StartOfTU);
+
+  // From now on, we have an owning module for all declarations we see.
+  // All of these are implicitly exported.
+  auto *TU = Context.getTranslationUnitDecl();
+  TU->setModuleOwnershipKind(Decl::ModuleOwnershipKind::Visible);
+  TU->setLocalOwningModule(Mod);
+}
+
 Sema::DeclGroupPtrTy
 Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc,
                       ModuleDeclKind MDK, ModuleIdPath Path,
@@ -149,6 +181,7 @@
     return nullptr;
 
   case LangOptions::CMK_HeaderModule:
+  case LangOptions::CMK_HeaderUnit:
     Diag(ModuleLoc, diag::err_module_decl_in_header_module);
     return nullptr;
   }
@@ -310,6 +343,7 @@
   case Module::GlobalModuleFragment:
   case Module::ModulePartitionImplementation:
   case Module::ModulePartitionInterface:
+  case Module::ModuleHeaderUnit:
     Diag(PrivateLoc, diag::err_private_module_fragment_not_module);
     return nullptr;
 
@@ -480,7 +514,13 @@
       Mod->Kind == Module::ModuleKind::ModulePartitionImplementation) {
     Diag(ExportLoc, diag::err_export_partition_impl)
         << SourceRange(ExportLoc, Path.back().second);
-  } else if (!ModuleScopes.empty() && ModuleScopes.back().ModuleInterface) {
+  } else if (!ModuleScopes.empty() &&
+             (ModuleScopes.back().ModuleInterface ||
+              (getLangOpts().CPlusPlusModules &&
+               ModuleScopes.back().Module->isGlobalModule()))) {
+    assert((!ModuleScopes.back().Module->isGlobalModule() ||
+            Mod->Kind == Module::ModuleKind::ModuleHeaderUnit) &&
+           "should only be importing a header unit into the GMF");
     // Re-export the module if the imported module is exported.
     // Note that we don't need to add re-exported module to Imports field
     // since `Exports` implies the module is imported already.
Index: clang/lib/Sema/Sema.cpp
===================================================================
--- clang/lib/Sema/Sema.cpp
+++ clang/lib/Sema/Sema.cpp
@@ -1030,9 +1030,13 @@
 /// is parsed. Note that the ASTContext may have already injected some
 /// declarations.
 void Sema::ActOnStartOfTranslationUnit() {
-  if (getLangOpts().ModulesTS &&
-      (getLangOpts().getCompilingModule() == LangOptions::CMK_ModuleInterface ||
-       getLangOpts().getCompilingModule() == LangOptions::CMK_None)) {
+  if (getLangOpts().CPlusPlusModules &&
+      getLangOpts().getCompilingModule() == LangOptions::CMK_HeaderUnit)
+    HandleStartOfHeaderUnit();
+  else if (getLangOpts().ModulesTS &&
+           (getLangOpts().getCompilingModule() ==
+                LangOptions::CMK_ModuleInterface ||
+            getLangOpts().getCompilingModule() == LangOptions::CMK_None)) {
     // We start in an implied global module fragment.
     SourceLocation StartOfTU =
         SourceMgr.getLocForStartOfFile(SourceMgr.getMainFileID());
Index: clang/lib/Parse/Parser.cpp
===================================================================
--- clang/lib/Parse/Parser.cpp
+++ clang/lib/Parse/Parser.cpp
@@ -2468,8 +2468,9 @@
     break;
   case Sema::ModuleImportState::GlobalFragment:
     // We can only have pre-processor directives in the global module
-    // fragment.  We can, however have a header unit import here.
-    if (!HeaderUnit)
+    // fragment.  We cannot import a named modules here, however we have a
+    // header unit import.
+    if (!HeaderUnit || HeaderUnit->Kind != Module::ModuleKind::ModuleHeaderUnit)
       Diag(ImportLoc, diag::err_import_in_wrong_fragment) << IsPartition << 0;
     else
       SeenError = false;
Index: clang/lib/Lex/ModuleMap.cpp
===================================================================
--- clang/lib/Lex/ModuleMap.cpp
+++ clang/lib/Lex/ModuleMap.cpp
@@ -905,6 +905,19 @@
   return Result;
 }
 
+Module *ModuleMap::createHeaderUnit(SourceLocation Loc, StringRef Name,
+                                    Module::Header H) {
+  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++);
+  Result->Kind = Module::ModuleHeaderUnit;
+  Modules[Name] = SourceModule = Result;
+  addHeader(Result, H, NormalHeader);
+  return Result;
+}
+
 /// For a framework module, infer the framework against which we
 /// should link.
 static void inferFrameworkLink(Module *Mod, const DirectoryEntry *FrameworkDir,
Index: clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
===================================================================
--- clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
+++ clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
@@ -67,6 +67,8 @@
     return std::make_unique<GenerateModuleInterfaceAction>();
   case GenerateHeaderModule:
     return std::make_unique<GenerateHeaderModuleAction>();
+  case GenerateHeaderUnit:
+    return std::make_unique<GenerateHeaderUnitAction>();
   case GeneratePCH:            return std::make_unique<GeneratePCHAction>();
   case GenerateInterfaceStubs:
     return std::make_unique<GenerateInterfaceStubsAction>();
Index: clang/lib/Frontend/FrontendActions.cpp
===================================================================
--- clang/lib/Frontend/FrontendActions.cpp
+++ clang/lib/Frontend/FrontendActions.cpp
@@ -336,6 +336,21 @@
   return CI.createDefaultOutputFile(/*Binary=*/true, InFile, "pcm");
 }
 
+bool GenerateHeaderUnitAction::BeginSourceFileAction(CompilerInstance &CI) {
+  if (!CI.getLangOpts().CPlusPlusModules) {
+    CI.getDiagnostics().Report(diag::err_module_interface_requires_cpp_modules);
+    return false;
+  }
+  CI.getLangOpts().setCompilingModule(LangOptions::CMK_HeaderUnit);
+  return GenerateModuleAction::BeginSourceFileAction(CI);
+}
+
+std::unique_ptr<raw_pwrite_stream>
+GenerateHeaderUnitAction::CreateOutputFile(CompilerInstance &CI,
+                                           StringRef InFile) {
+  return CI.createDefaultOutputFile(/*Binary=*/true, InFile, "pcm");
+}
+
 SyntaxOnlyAction::~SyntaxOnlyAction() {
 }
 
@@ -818,6 +833,8 @@
     return "Partition Interface";
   case Module::ModulePartitionImplementation:
     return "Partition Implementation";
+  case Module::ModuleHeaderUnit:
+    return "Header Unit";
   case Module::GlobalModuleFragment:
     return "Global Module Fragment";
   case Module::PrivateModuleFragment:
Index: clang/lib/Frontend/CompilerInvocation.cpp
===================================================================
--- clang/lib/Frontend/CompilerInvocation.cpp
+++ clang/lib/Frontend/CompilerInvocation.cpp
@@ -2420,6 +2420,7 @@
       {frontend::GenerateModule, OPT_emit_module},
       {frontend::GenerateModuleInterface, OPT_emit_module_interface},
       {frontend::GenerateHeaderModule, OPT_emit_header_module},
+      {frontend::GenerateHeaderUnit, OPT_emit_header_unit},
       {frontend::GeneratePCH, OPT_emit_pch},
       {frontend::GenerateInterfaceStubs, OPT_emit_interface_stubs},
       {frontend::InitOnly, OPT_init_only},
@@ -2436,7 +2437,7 @@
       {frontend::MigrateSource, OPT_migrate},
       {frontend::RunPreprocessorOnly, OPT_Eonly},
       {frontend::PrintDependencyDirectivesSourceMinimizerOutput,
-          OPT_print_dependency_directives_minimized_source},
+       OPT_print_dependency_directives_minimized_source},
   };
 
   return Table;
@@ -4160,6 +4161,7 @@
   case frontend::GenerateModule:
   case frontend::GenerateModuleInterface:
   case frontend::GenerateHeaderModule:
+  case frontend::GenerateHeaderUnit:
   case frontend::GeneratePCH:
   case frontend::GenerateInterfaceStubs:
   case frontend::ParseSyntaxOnly:
Index: clang/lib/AST/Decl.cpp
===================================================================
--- clang/lib/AST/Decl.cpp
+++ clang/lib/AST/Decl.cpp
@@ -1561,6 +1561,7 @@
   case Module::ModulePartitionImplementation:
     return M;
 
+  case Module::ModuleHeaderUnit:
   case Module::GlobalModuleFragment: {
     // External linkage declarations in the global module have no owning module
     // for linkage purposes. But internal linkage declarations in the global
@@ -1576,7 +1577,8 @@
       InternalLinkage = !ND->hasExternalFormalLinkage();
     else
       InternalLinkage = isInAnonymousNamespace();
-    return InternalLinkage ? M->Parent : nullptr;
+    return InternalLinkage ? M->Kind == Module::ModuleHeaderUnit ? M : M->Parent
+                           : nullptr;
   }
 
   case Module::PrivateModuleFragment:
Index: clang/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ clang/include/clang/Sema/Sema.h
@@ -2967,6 +2967,12 @@
     NotACXX20Module  ///< Not a C++20 TU, or an invalid state was found.
   };
 
+private:
+  /// The parser has begun a translation unit to be compiled as a C++20
+  /// Header Unit, helper for ActOnStartOfTranslationUnit() only.
+  void HandleStartOfHeaderUnit();
+
+public:
   /// The parser has processed a module-declaration that begins the definition
   /// of a module interface or implementation.
   DeclGroupPtrTy ActOnModuleDecl(SourceLocation StartLoc,
Index: clang/include/clang/Lex/ModuleMap.h
===================================================================
--- clang/include/clang/Lex/ModuleMap.h
+++ clang/include/clang/Lex/ModuleMap.h
@@ -564,6 +564,10 @@
   /// Create a header module from the specified list of headers.
   Module *createHeaderModule(StringRef Name, ArrayRef<Module::Header> Headers);
 
+  /// Create a C++20 header unit.
+  Module *createHeaderUnit(SourceLocation Loc, StringRef Name,
+                           Module::Header H);
+
   /// Infer the contents of a framework module map from the given
   /// framework directory.
   Module *inferFrameworkModule(const DirectoryEntry *FrameworkDir,
Index: clang/include/clang/Frontend/FrontendOptions.h
===================================================================
--- clang/include/clang/Frontend/FrontendOptions.h
+++ clang/include/clang/Frontend/FrontendOptions.h
@@ -90,6 +90,9 @@
   /// Generate pre-compiled module from a set of header files.
   GenerateHeaderModule,
 
+  /// Generate a C++20 header unit module from a header file.
+  GenerateHeaderUnit,
+
   /// Generate pre-compiled header.
   GeneratePCH,
 
Index: clang/include/clang/Frontend/FrontendActions.h
===================================================================
--- clang/include/clang/Frontend/FrontendActions.h
+++ clang/include/clang/Frontend/FrontendActions.h
@@ -168,6 +168,15 @@
   CreateOutputFile(CompilerInstance &CI, StringRef InFile) override;
 };
 
+class GenerateHeaderUnitAction : public GenerateModuleAction {
+
+private:
+  bool BeginSourceFileAction(CompilerInstance &CI) override;
+
+  std::unique_ptr<raw_pwrite_stream>
+  CreateOutputFile(CompilerInstance &CI, StringRef InFile) override;
+};
+
 class SyntaxOnlyAction : public ASTFrontendAction {
 protected:
   std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
Index: clang/include/clang/Driver/Options.td
===================================================================
--- clang/include/clang/Driver/Options.td
+++ clang/include/clang/Driver/Options.td
@@ -5639,6 +5639,8 @@
   HelpText<"Generate pre-compiled module file from a C++ module interface">;
 def emit_header_module : Flag<["-"], "emit-header-module">,
   HelpText<"Generate pre-compiled module file from a set of header files">;
+def emit_header_unit : Flag<["-"], "emit-header-unit">,
+  HelpText<"Generate C++20 header units from header files">;
 def emit_pch : Flag<["-"], "emit-pch">,
   HelpText<"Generate pre-compiled header file">;
 def emit_llvm_bc : Flag<["-"], "emit-llvm-bc">,
Index: clang/include/clang/Basic/Module.h
===================================================================
--- clang/include/clang/Basic/Module.h
+++ clang/include/clang/Basic/Module.h
@@ -109,6 +109,9 @@
     /// This is a C++20 module interface unit.
     ModuleInterfaceUnit,
 
+    /// This is a C++ 20 header unit.
+    ModuleHeaderUnit,
+
     /// This is a C++ 20 module partition interface.
     ModulePartitionInterface,
 
Index: clang/include/clang/Basic/LangOptions.h
===================================================================
--- clang/include/clang/Basic/LangOptions.h
+++ clang/include/clang/Basic/LangOptions.h
@@ -90,6 +90,9 @@
     /// Compiling a module from a list of header files.
     CMK_HeaderModule,
 
+    /// Compiling a module header unit.
+    CMK_HeaderUnit,
+
     /// Compiling a C++ modules TS module interface unit.
     CMK_ModuleInterface,
   };
Index: clang/include/clang/Basic/LangOptions.def
===================================================================
--- clang/include/clang/Basic/LangOptions.def
+++ clang/include/clang/Basic/LangOptions.def
@@ -166,7 +166,7 @@
 LANGOPT(Modules           , 1, 0, "modules semantics")
 COMPATIBLE_LANGOPT(ModulesTS  , 1, 0, "C++ Modules TS syntax")
 COMPATIBLE_LANGOPT(CPlusPlusModules, 1, 0, "C++ modules syntax")
-BENIGN_ENUM_LANGOPT(CompilingModule, CompilingModuleKind, 2, CMK_None,
+BENIGN_ENUM_LANGOPT(CompilingModule, CompilingModuleKind, 3, CMK_None,
                     "compiling a module interface")
 BENIGN_LANGOPT(CompilingPCH, 1, 0, "building a pch")
 BENIGN_LANGOPT(BuildingPCHWithObjectFile, 1, 0, "building a pch which has a corresponding object file")
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to