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

amend a testcase.

I reordered and squashed two of the patches before posting, and failed to spot 
that a test case had
a (non-critical) dependency on functionality introduced by a later patch.  
Fixed by removing the
dependency.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D121096

Files:
  clang/include/clang/Frontend/FrontendOptions.h
  clang/lib/Frontend/CompilerInvocation.cpp
  clang/lib/Frontend/FrontendAction.cpp
  clang/test/Modules/cxx20-hu-02.cpp
  clang/test/Modules/cxx20-hu-03.cpp

Index: clang/test/Modules/cxx20-hu-03.cpp
===================================================================
--- /dev/null
+++ clang/test/Modules/cxx20-hu-03.cpp
@@ -0,0 +1,57 @@
+// Test check that processing headers as C++20 units allows #pragma once.
+
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+// RUN: split-file %s %t
+// RUN: cd %t
+
+// RUN: %clang_cc1 -std=c++20 -emit-header-unit -xc++-user-header hu-01.h \
+// RUN: -Werror -o hu-01.pcm
+
+// RUN: %clang_cc1 -std=c++20 -emit-header-unit -xc++-user-header hu-02.h \
+// RUN: -fmodule-file=%t/hu-01.pcm -o hu-02.pcm
+
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only imports-01.cpp \
+// RUN: -fmodule-file=%t/hu-01.pcm
+
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only imports-02.cpp \
+// RUN: -fmodule-file=%t/hu-02.pcm
+
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only imports-03.cpp \
+// RUN: -fmodule-file=%t/hu-02.pcm
+
+//--- hu-01.h
+#pragma once
+struct HU {
+  int a;
+};
+// expected-no-diagnostics
+
+//--- hu-02.h
+export import "hu-01.h";
+// expected-no-diagnostics
+
+//--- imports-01.cpp
+import "hu-01.h";
+
+HU foo(int x) {
+  return {x};
+}
+// expected-no-diagnostics
+
+//--- imports-02.cpp
+import "hu-02.h";
+
+HU foo(int x) {
+  return {x};
+}
+// expected-no-diagnostics
+
+//--- imports-03.cpp
+import "hu-01.h";
+import "hu-02.h";
+
+HU foo(int x) {
+  return {x};
+}
+// expected-no-diagnostics
Index: clang/test/Modules/cxx20-hu-02.cpp
===================================================================
--- /dev/null
+++ clang/test/Modules/cxx20-hu-02.cpp
@@ -0,0 +1,77 @@
+// Test generation and import of user and system C++20 Header Units.
+
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+// RUN: split-file %s %t
+// RUN: cd %t
+
+// check user path
+// RUN: %clang_cc1 -std=c++20 -emit-header-unit -I user \
+// RUN: -xc++-user-header hu-01.h -o hu-01.pcm
+
+// RUN: %clang_cc1 -std=c++20 -module-file-info hu-01.pcm | \
+// RUN: FileCheck --check-prefix=CHECK-HU %s -DTDIR=%t
+
+// RUN: %clang_cc1 -std=c++20 -emit-module-interface imp-hu-01.cpp \
+// RUN:  -I user -fmodule-file=hu-01.pcm -o B.pcm -Rmodule-import \
+// RUN: 2>&1  | FileCheck --check-prefix=CHECK-IMP %s -DTDIR=%t
+
+// check system path
+// RUN: %clang_cc1 -std=c++20 -emit-header-unit -isystem system \
+// RUN: -xc++-system-header hu-02.h -o hu-02.pcm
+
+// RUN: %clang_cc1 -std=c++20 -module-file-info hu-02.pcm | \
+// RUN: FileCheck --check-prefix=CHECK-HU2 %s -DTDIR=%t
+
+// RUN: %clang_cc1 -std=c++20 -emit-module-interface imp-hu-02.cpp \
+// RUN:  -isystem system -fmodule-file=hu-02.pcm -o C.pcm \
+// RUN: -Rmodule-import 2>&1 | \
+// RUN: FileCheck --check-prefix=CHECK-SYS-IMP %s -DTDIR=%t
+
+// check absolute path
+// RUN: %clang_cc1 -std=c++20 -emit-header-unit  \
+// RUN: -xc++-header-unit-header %t/hu-03.h -o hu-03.pcm
+
+// RUN: %clang_cc1 -std=c++20 -module-file-info hu-03.pcm | \
+// RUN: FileCheck --check-prefix=CHECK-HU3 %s -DTDIR=%t
+
+//--- user/hu-01.h
+int foo(int);
+
+// CHECK-HU:  ====== C++20 Module structure ======
+// CHECK-HU-NEXT:  Header Unit 'user/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 'user/hu-01.h' from 'hu-01.pcm'
+// expected-no-diagnostics
+
+//--- system/hu-02.h
+int baz(int);
+
+// CHECK-HU2:  ====== C++20 Module structure ======
+// CHECK-HU2-NEXT:  Header Unit 'system/hu-02.h' is the Primary Module at index #1
+
+//--- imp-hu-02.cpp
+module;
+import <hu-02.h>;
+
+export module C;
+
+int bar(int x) {
+  return baz(x);
+}
+// CHECK-SYS-IMP: remark: importing module 'system/hu-02.h' from 'hu-02.pcm'
+// expected-no-diagnostics
+
+//--- hu-03.h
+int curly(int);
+
+// CHECK-HU3:  ====== C++20 Module structure ======
+// CHECK-HU3-NEXT:  Header Unit '[[TDIR]]/hu-03.h' is the Primary Module at index #1
+// expected-no-diagnostics
Index: clang/lib/Frontend/FrontendAction.cpp
===================================================================
--- clang/lib/Frontend/FrontendAction.cpp
+++ clang/lib/Frontend/FrontendAction.cpp
@@ -798,7 +798,48 @@
                                            &CI.getPreprocessor());
   HasBegunSourceFile = true;
 
-  // Initialize the main file entry.
+  // Handle C++20 header units.
+  // Here, the user has the option to specify that the header name should be
+  // looked up in the pre-processor search paths (and the main filename as
+  // passed by the driver might therefore be incomplete until that look-up).
+  if (CI.getLangOpts().CPlusPlusModules && Input.getKind().isHeaderUnit() &&
+      !Input.getKind().isPreprocessed()) {
+    StringRef FileName = Input.getFile();
+    InputKind Kind = Input.getKind();
+    if (Kind.getHeaderUnit() != InputKind::HeaderUnit_Abs) {
+      assert(CI.hasPreprocessor() &&
+             "trying to build a header unit without a Pre-processor?");
+      HeaderSearch &HS = CI.getPreprocessor().getHeaderSearchInfo();
+      // Relative searches begin from CWD.
+      const DirectoryEntry *Dir = nullptr;
+      if (auto DirOrErr = CI.getFileManager().getDirectory("."))
+        Dir = *DirOrErr;
+      SmallVector<std::pair<const FileEntry *, const DirectoryEntry *>, 1> CWD;
+      CWD.push_back({nullptr, Dir});
+      Optional<FileEntryRef> FE =
+          HS.LookupFile(FileName, SourceLocation(),
+                        /*Angled*/ Input.getKind().getHeaderUnit() ==
+                            InputKind::HeaderUnit_System,
+                        nullptr, nullptr, CWD, nullptr, nullptr, nullptr,
+                        nullptr, nullptr, nullptr);
+      if (!FE) {
+        CI.getDiagnostics().Report(diag::err_module_header_file_not_found)
+            << FileName;
+        return false;
+      }
+      // We now have the filename...
+      FileName = FE->getFileEntry().getName();
+      // ... still a header unit, but now use the path as written.
+      Kind = Input.getKind().withHeaderUnit(InputKind::HeaderUnit_Abs);
+      Input = FrontendInputFile(FileName, Kind, Input.isSystem());
+    }
+    // Unless the user has overridden the name, the header unit module name is
+    // the pathname for the file.
+    if (CI.getLangOpts().ModuleName.empty())
+      CI.getLangOpts().ModuleName = std::string(FileName);
+    CI.getLangOpts().CurrentModule = CI.getLangOpts().ModuleName;
+  }
+
   if (!CI.InitializeSourceManager(Input))
     return false;
 
Index: clang/lib/Frontend/CompilerInvocation.cpp
===================================================================
--- clang/lib/Frontend/CompilerInvocation.cpp
+++ clang/lib/Frontend/CompilerInvocation.cpp
@@ -2567,6 +2567,20 @@
     StringRef Preprocessed = Opts.DashX.isPreprocessed() ? "-cpp-output" : "";
     StringRef ModuleMap =
         Opts.DashX.getFormat() == InputKind::ModuleMap ? "-module-map" : "";
+    StringRef HeaderUnit = "";
+    switch (Opts.DashX.getHeaderUnit()) {
+    case InputKind::HeaderUnit_None:
+      break;
+    case InputKind::HeaderUnit_User:
+      HeaderUnit = "-user";
+      break;
+    case InputKind::HeaderUnit_System:
+      HeaderUnit = "-system";
+      break;
+    case InputKind::HeaderUnit_Abs:
+      HeaderUnit = "-header-unit";
+      break;
+    }
     StringRef Header = IsHeader ? "-header" : "";
 
     StringRef Lang;
@@ -2611,7 +2625,8 @@
       break;
     }
 
-    GenerateArg(Args, OPT_x, Lang + Header + ModuleMap + Preprocessed, SA);
+    GenerateArg(Args, OPT_x,
+                Lang + HeaderUnit + Header + ModuleMap + Preprocessed, SA);
   }
 
   // OPT_INPUT has a unique class, generate it directly.
@@ -2756,13 +2771,30 @@
   if (const Arg *A = Args.getLastArg(OPT_x)) {
     StringRef XValue = A->getValue();
 
-    // Parse suffixes: '<lang>(-header|[-module-map][-cpp-output])'.
+    // Parse suffixes:
+    // '<lang>(-[{header-unit,user,system}-]header|[-module-map][-cpp-output])'.
     // FIXME: Supporting '<lang>-header-cpp-output' would be useful.
     bool Preprocessed = XValue.consume_back("-cpp-output");
     bool ModuleMap = XValue.consume_back("-module-map");
-    IsHeaderFile = !Preprocessed && !ModuleMap &&
-                   XValue != "precompiled-header" &&
-                   XValue.consume_back("-header");
+    // Detect and consume the header indicator.
+    bool IsHeader =
+        XValue != "precompiled-header" && XValue.consume_back("-header");
+
+    // If we have c++-{user,system}-header, that indicates a header unit input
+    // likewise, if the user put -fmodule-header together with a header with an
+    // absolute path (header-unit-header).
+    InputKind::HeaderUnitKind HUK = InputKind::HeaderUnit_None;
+    if (IsHeader || Preprocessed) {
+      HUK =
+          XValue.consume_back("-header-unit") ? InputKind::HeaderUnit_Abs : HUK;
+      HUK = XValue.consume_back("-system") ? InputKind::HeaderUnit_System : HUK;
+      HUK = XValue.consume_back("-user") ? InputKind::HeaderUnit_User : HUK;
+    }
+
+    // The value set by this processing is an un-preprocessed source which is
+    // not intended to be a module map or header unit.
+    IsHeaderFile = IsHeader && !Preprocessed && !ModuleMap &&
+                   HUK == InputKind::HeaderUnit_None;
 
     // Principal languages.
     DashX = llvm::StringSwitch<InputKind>(XValue)
@@ -2779,14 +2811,16 @@
 
     // "objc[++]-cpp-output" is an acceptable synonym for
     // "objective-c[++]-cpp-output".
-    if (DashX.isUnknown() && Preprocessed && !IsHeaderFile && !ModuleMap)
+    if (DashX.isUnknown() && Preprocessed && !IsHeaderFile && !ModuleMap &&
+        HUK == InputKind::HeaderUnit_None)
       DashX = llvm::StringSwitch<InputKind>(XValue)
                   .Case("objc", Language::ObjC)
                   .Case("objc++", Language::ObjCXX)
                   .Default(Language::Unknown);
 
     // Some special cases cannot be combined with suffixes.
-    if (DashX.isUnknown() && !Preprocessed && !ModuleMap && !IsHeaderFile)
+    if (DashX.isUnknown() && !Preprocessed && !IsHeaderFile && !ModuleMap &&
+        HUK == InputKind::HeaderUnit_None)
       DashX = llvm::StringSwitch<InputKind>(XValue)
                   .Case("cpp-output", InputKind(Language::C).getPreprocessed())
                   .Case("assembler-with-cpp", Language::Asm)
@@ -2801,6 +2835,13 @@
 
     if (Preprocessed)
       DashX = DashX.getPreprocessed();
+    // A regular header is considered mutually exclusive with a header unit
+    // one
+    if (HUK != InputKind::HeaderUnit_None) {
+      DashX = DashX.withHeaderUnit(HUK);
+      IsHeaderFile = true;
+    } else if (IsHeaderFile)
+      DashX = DashX.getHeader();
     if (ModuleMap)
       DashX = DashX.withFormat(InputKind::ModuleMap);
   }
@@ -2810,6 +2851,11 @@
   Opts.Inputs.clear();
   if (Inputs.empty())
     Inputs.push_back("-");
+
+  assert((DashX.getHeaderUnit() == InputKind::HeaderUnit_None ||
+          Inputs.size() == 1) &&
+         "Expected only one input file for header unit");
+
   for (unsigned i = 0, e = Inputs.size(); i != e; ++i) {
     InputKind IK = DashX;
     if (IK.isUnknown()) {
@@ -3857,6 +3903,7 @@
   }
   if (Opts.FastRelaxedMath)
     Opts.setDefaultFPContractMode(LangOptions::FPM_Fast);
+
   llvm::sort(Opts.ModuleFeatures);
 
   // -mrtd option
Index: clang/include/clang/Frontend/FrontendOptions.h
===================================================================
--- clang/include/clang/Frontend/FrontendOptions.h
+++ clang/include/clang/Frontend/FrontendOptions.h
@@ -153,6 +153,8 @@
   Language Lang;
   unsigned Fmt : 3;
   unsigned Preprocessed : 1;
+  unsigned HeaderUnit : 3;
+  unsigned Header : 1;
 
 public:
   /// The input file format.
@@ -162,13 +164,29 @@
     Precompiled
   };
 
+  // If we are building a header unit, what kind it is; this affects whether
+  // we look for the file in the user or system include search paths before
+  // flagging a missing input.
+  enum HeaderUnitKind {
+    HeaderUnit_None,
+    HeaderUnit_User,
+    HeaderUnit_System,
+    HeaderUnit_Abs
+  };
+
   constexpr InputKind(Language L = Language::Unknown, Format F = Source,
-                      bool PP = false)
-      : Lang(L), Fmt(F), Preprocessed(PP) {}
+                      bool PP = false, HeaderUnitKind HU = HeaderUnit_None,
+                      bool HD = false)
+      : Lang(L), Fmt(F), Preprocessed(PP), HeaderUnit(HU), Header(HD) {}
 
   Language getLanguage() const { return static_cast<Language>(Lang); }
   Format getFormat() const { return static_cast<Format>(Fmt); }
+  HeaderUnitKind getHeaderUnit() const {
+    return static_cast<HeaderUnitKind>(HeaderUnit);
+  }
   bool isPreprocessed() const { return Preprocessed; }
+  bool isHeader() const { return Header; }
+  bool isHeaderUnit() const { return HeaderUnit != HeaderUnit_None; }
 
   /// Is the input kind fully-unknown?
   bool isUnknown() const { return Lang == Language::Unknown && Fmt == Source; }
@@ -179,11 +197,23 @@
   }
 
   InputKind getPreprocessed() const {
-    return InputKind(getLanguage(), getFormat(), true);
+    return InputKind(getLanguage(), getFormat(), true, getHeaderUnit(),
+                     isHeader());
+  }
+
+  InputKind getHeader() const {
+    return InputKind(getLanguage(), getFormat(), isPreprocessed(),
+                     getHeaderUnit(), true);
+  }
+
+  InputKind withHeaderUnit(HeaderUnitKind HU) const {
+    return InputKind(getLanguage(), getFormat(), isPreprocessed(), HU,
+                     isHeader());
   }
 
   InputKind withFormat(Format F) const {
-    return InputKind(getLanguage(), F, isPreprocessed());
+    return InputKind(getLanguage(), F, isPreprocessed(), getHeaderUnit(),
+                     isHeader());
   }
 };
 
@@ -218,6 +248,10 @@
   bool isFile() const { return !isBuffer(); }
   bool isBuffer() const { return Buffer != None; }
   bool isPreprocessed() const { return Kind.isPreprocessed(); }
+  bool isHeader() const { return Kind.isHeader(); }
+  InputKind::HeaderUnitKind getHeaderUnit() const {
+    return Kind.getHeaderUnit();
+  }
 
   StringRef getFile() const {
     assert(isFile());
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to