mikerice created this revision.
mikerice added reviewers: rnk, thakis, erichkeane, cfe-commits.

Implement support for MS-style PCH through headers.

This enables support for /Yc and /Yu where the through header is either
on the command line or included in the source.  It replaces the current
support the requires the header also be specified with /FI.

This change adds a -cc1 option -pch-through-header that is used to either
start or stop compilation during PCH create or use.

When creating a PCH, the compilation ends after compilation of the through
header.

When using a PCH, tokens are skipped until after the through header is seen.


Repository:
  rC Clang

https://reviews.llvm.org/D46652

Files:
  include/clang/Basic/DiagnosticLexKinds.td
  include/clang/Driver/CC1Options.td
  include/clang/Frontend/FrontendOptions.h
  include/clang/Lex/Preprocessor.h
  include/clang/Lex/PreprocessorOptions.h
  lib/Driver/Driver.cpp
  lib/Driver/ToolChains/Clang.cpp
  lib/Frontend/CompilerInstance.cpp
  lib/Frontend/CompilerInvocation.cpp
  lib/Lex/PPDirectives.cpp
  lib/Lex/PPLexerChange.cpp
  lib/Lex/Preprocessor.cpp
  lib/Parse/ParseAST.cpp
  lib/Serialization/ASTReader.cpp
  test/Driver/cl-pch-search.cpp
  test/Driver/cl-pch.cpp
  test/PCH/Inputs/pch-through-use0.cpp
  test/PCH/Inputs/pch-through-use1.cpp
  test/PCH/Inputs/pch-through-use2.cpp
  test/PCH/Inputs/pch-through1.h
  test/PCH/Inputs/pch-through2.h
  test/PCH/Inputs/pch-through3.h
  test/PCH/Inputs/pch-through4.h
  test/PCH/pch-through.cpp

Index: test/PCH/pch-through.cpp
===================================================================
--- /dev/null
+++ test/PCH/pch-through.cpp
@@ -0,0 +1,72 @@
+// Through header not found (anywhere)
+// RUN: not %clang_cc1 -emit-pch \
+// RUN:   -pch-through-header=Inputs/pch-does-not-exist.h -o %t %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-TEST0A %s
+// CHECK-TEST0A: fatal error:{{.*}} 'Inputs/pch-does-not-exist.h'
+// CHECK-TEST0A-SAME: required for precompiled header not found
+
+// Through header not found in search path
+// RUN: not %clang_cc1 -emit-pch \
+// RUN:   -pch-through-header=Inputs/pch-through2.h -o %t \
+// RUN:   %S/Inputs/pch-through-use0.cpp 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-TEST0B %s
+// CHECK-TEST0B: fatal error:{{.*}}'Inputs/pch-through2.h'
+// CHECK-TEST0B-SAME: required for precompiled header not found
+
+// No #include of through header during pch create
+// RUN: not %clang_cc1 -DSOURCE1 -I %S -emit-pch \
+// RUN:   -pch-through-header=Inputs/pch-through2.h -o %t %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-TEST1A %s
+// CHECK-TEST1A: fatal error:{{.*}} #include of
+// CHECK-TEST1A-SAME: 'Inputs/pch-through2.h' not seen while attempting to
+// CHECK-TEST1A-SAME: create precompiled header
+
+#ifdef SOURCE1
+#endif
+
+// Create
+// RUN: %clang_cc1 -DSOURCE2 -I %S -emit-pch \
+// RUN:   -pch-through-header=Inputs/pch-through2.h -o %t.s2t2 %s
+
+// Use
+// RUN: %clang_cc1 -DSOURCE2 -I %S -include-pch %t.s2t2 \
+// RUN:   -pch-through-header=Inputs/pch-through2.h %s
+
+#ifdef SOURCE2
+#include "Inputs/pch-through1.h"
+#include "Inputs/pch-through2.h"
+#endif
+
+// No #include of through header during pch use
+// RUN: not %clang_cc1 -I %S -include-pch %t.s2t2 \
+// RUN:   -pch-through-header=Inputs/pch-through2.h \
+// RUN:   %S/Inputs/pch-through-use1.cpp 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-TEST2A %s
+// CHECK-TEST2A: fatal error:{{.*}} #include of
+// CHECK-TEST2A-SAME: 'Inputs/pch-through2.h' not seen while attempting to
+// CHECK-TEST2A-SAME: use precompiled header
+
+// check that pch only contains code before the through header.
+// RUN: %clang_cc1 -DSOURCE2 -I %S -emit-pch \
+// RUN:   -pch-through-header=Inputs/pch-through1.h -o %t.s2t1 %s
+// RUN: not %clang_cc1 -I %S -include-pch %t.s2t1 \
+// RUN:   -pch-through-header=Inputs/pch-through1.h \
+// RUN:   %S/Inputs/pch-through-use1.cpp 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-TEST3 %s
+// CHECK-TEST3: error: use of undeclared identifier 'through2'
+
+#ifdef SOURCE3
+#endif
+
+// checks for through headers that are also -includes
+// RUN: %clang_cc1 -DSOURCE3 -I %S -include Inputs/pch-through1.h \
+// RUN:   -pch-through-header=Inputs/pch-through1.h -emit-pch -o %t.s3t1 %s
+// RUN: %clang_cc1 -DSOURCE3 -I %S -include Inputs/pch-through1.h \
+// RUN:   -include Inputs/pch-through2.h -include Inputs/pch-through3.h \
+// RUN:   -pch-through-header=Inputs/pch-through2.h -emit-pch -o %t.s3t2 %s
+// Use through header from -includes
+// RUN: %clang_cc1 -DSOURCE3 -I %S -include Inputs/pch-through1.h \
+// RUN:   -include Inputs/pch-through2.h -include Inputs/pch-through4.h \
+// RUN:   -pch-through-header=Inputs/pch-through2.h -include-pch %t.s3t2 \
+// RUN:   %S/Inputs/pch-through-use2.cpp -o %t.out
+
Index: test/PCH/Inputs/pch-through4.h
===================================================================
--- /dev/null
+++ test/PCH/Inputs/pch-through4.h
@@ -0,0 +1,2 @@
+#define THROUGH4
+int through4(int);
Index: test/PCH/Inputs/pch-through3.h
===================================================================
--- /dev/null
+++ test/PCH/Inputs/pch-through3.h
@@ -0,0 +1,2 @@
+#define THROUGH3
+int through3(int);
Index: test/PCH/Inputs/pch-through2.h
===================================================================
--- /dev/null
+++ test/PCH/Inputs/pch-through2.h
@@ -0,0 +1,2 @@
+#define THROUGH2
+int through2(int);
Index: test/PCH/Inputs/pch-through1.h
===================================================================
--- /dev/null
+++ test/PCH/Inputs/pch-through1.h
@@ -0,0 +1,2 @@
+#define THROUGH1
+int through1(int);
Index: test/PCH/Inputs/pch-through-use2.cpp
===================================================================
--- /dev/null
+++ test/PCH/Inputs/pch-through-use2.cpp
@@ -0,0 +1,3 @@
+void foo() {
+  through4(0);
+}
Index: test/PCH/Inputs/pch-through-use1.cpp
===================================================================
--- /dev/null
+++ test/PCH/Inputs/pch-through-use1.cpp
@@ -0,0 +1,5 @@
+#include "Inputs/pch-through1.h"
+#include "Inputs/pch-through3.h"
+void foo() {
+  through2(0);
+}
Index: test/PCH/Inputs/pch-through-use0.cpp
===================================================================
--- /dev/null
+++ test/PCH/Inputs/pch-through-use0.cpp
@@ -0,0 +1,2 @@
+void foo() {
+}
Index: test/Driver/cl-pch.cpp
===================================================================
--- test/Driver/cl-pch.cpp
+++ test/Driver/cl-pch.cpp
@@ -151,65 +151,85 @@
 // 1. Build .pch file: Includes foo1.h (but NOT foo3.h) and compiles foo2.h
 // CHECK-YCFIFIFI: cc1
 // CHECK-YCFIFIFI: -emit-pch
+// CHECK-YCFIFIFI: -pch-through-header=foo2.h
 // CHECK-YCFIFIFI: -include
 // CHECK-YCFIFIFI: foo1.h
-// CHECK-YCFIFIFI-NOT: foo2.h
-// CHECK-YCFIFIFI-NOT: foo3.h
+// CHECK-YCFIFIFI: -include
+// CHECK-YCFIFIFI: foo2.h
+// CHECK-YCFIFIFI: -include
+// CHECK-YCFIFIFI: foo3.h
 // CHECK-YCFIFIFI: -o
 // CHECK-YCFIFIFI: foo2.pch
 // CHECK-YCFIFIFI: -x
 // CHECK-YCFIFIFI: "c++-header"
-// CHECK-YCFIFIFI: foo2.h
+// CHECK-YCFIFIFI: cl-pch.cpp
 // 2. Use .pch file: Inlucdes foo2.pch and foo3.h
 // CHECK-YCFIFIFI: cc1
 // CHECK-YCFIFIFI: -emit-obj
-// CHECK-YCFIFIFI-NOT: foo1.h
-// CHECK-YCFIFIFI-NOT: foo2.h
 // CHECK-YCFIFIFI: -include-pch
 // CHECK-YCFIFIFI: foo2.pch
+// CHECK-YCFIFIFI: -pch-through-header=foo2.h
+// CHECK-YCFIFIFI: -include
+// CHECK-YCFIFIFI: foo1.h
+// CHECK-YCFIFIFI: -include
+// CHECK-YCFIFIFI: foo2.h
 // CHECK-YCFIFIFI: -include
 // CHECK-YCFIFIFI: foo3.h
+// CHECK-YCFIFIFI: -o
+// CHECK-YCFIFIFI: cl-pch.obj
+// CHECK-YCFIFIFI: -x
+// CHECK-YCFIFIFI: "c++"
+// CHECK-YCFIFIFI: cl-pch.cpp
 
-// /Yucfoo2.h /FIfoo1.h /FIfoo2.h /FIfoo3.h
+// /Yufoo2.h /FIfoo1.h /FIfoo2.h /FIfoo3.h
 // => foo1 foo2 filtered out, foo3 into main compilation
 // RUN: %clang_cl -Werror /Yufoo2.h /FIfoo1.h /FIfoo2.h /FIfoo3.h /c -### -- %s 2>&1 \
 // RUN:   | FileCheck -check-prefix=CHECK-YUFIFIFI %s
 // Use .pch file, but don't build it.
 // CHECK-YUFIFIFI-NOT: -emit-pch
 // CHECK-YUFIFIFI: cc1
 // CHECK-YUFIFIFI: -emit-obj
-// CHECK-YUFIFIFI-NOT: foo1.h
-// CHECK-YUFIFIFI-NOT: foo2.h
 // CHECK-YUFIFIFI: -include-pch
 // CHECK-YUFIFIFI: foo2.pch
+// CHECK-YUFIFIFI: -pch-through-header=foo2.h
+// CHECK-YUFIFIFI: -include
+// CHECK-YUFIFIFI: foo1.h
+// CHECK-YUFIFIFI: -include
+// CHECK-YUFIFIFI: foo2.h
 // CHECK-YUFIFIFI: -include
 // CHECK-YUFIFIFI: foo3.h
 
-// FIXME: Implement support for /Ycfoo.h / /Yufoo.h without /FIfoo.h
+// Test /Ycfoo.h / /Yufoo.h without /FIfoo.h
 // RUN: %clang_cl -Werror /Ycfoo.h /c -### -- %s 2>&1 \
 // RUN:   | FileCheck -check-prefix=CHECK-YC-NOFI %s
-// CHECK-YC-NOFI: error: support for '/Yc' without a corresponding /FI flag not implemented yet; flag ignored
+// 1. Precompile
+// CHECK-YC-NOFI: cc1
+// CHECK-YC-NOFI: -emit-pch
+// CHECK-YC-NOFI: -pch-through-header=foo.h
+// CHECK-YC-NOFI: -o
+// CHECK-YC-NOFI: foo.pch
+// CHECK-YC-NOFI: -x
+// CHECK-YC-NOFI: c++-header
+// CHECK-YC-NOFI: cl-pch.cpp
+// 2. Build PCH object
+// CHECK-YC-NOFI: cc1
+// CHECK-YC-NOFI: -emit-obj
+// CHECK-YC-NOFI: -include-pch
+// CHECK-YC-NOFI: foo.pch
+// CHECK-YC-NOFI: -pch-through-header=foo.h
+// CHECK-YC-NOFI: -x
+// CHECK-YC-NOFI: c++
+// CHECK-YC-NOFI: cl-pch.cpp
 // RUN: %clang_cl -Werror /Yufoo.h /c -### -- %s 2>&1 \
 // RUN:   | FileCheck -check-prefix=CHECK-YU-NOFI %s
-// CHECK-YU-NOFI: error: support for '/Yu' without a corresponding /FI flag not implemented yet; flag ignored
-
-// /Yc and /FI relative to /I paths...
-// The rules are:
-// Yu/Yc and FI parameter must match exactly, else it's not found
-// Must match literally exactly: /FI./foo.h /Ycfoo.h does _not_ work.
-// However, the path can be relative to /I paths.
-// FIXME: Update the error messages below once /FI is no longer required, but
-// these test cases all should stay failures as they fail with cl.exe.
-
-// Check that ./ isn't canonicalized away.
-// RUN: %clang_cl -Werror /Ycpchfile.h /FI./pchfile.h /c -### -- %s 2>&1 \
-// RUN:   | FileCheck -check-prefix=CHECK-YC-I1 %s
-// CHECK-YC-I1: support for '/Yc' without a corresponding /FI flag not implemented yet; flag ignored
-
-// Check that ./ isn't canonicalized away.
-// RUN: %clang_cl -Werror /Yc./pchfile.h /FIpchfile.h /c -### -- %s 2>&1 \
-// RUN:   | FileCheck -check-prefix=CHECK-YC-I2 %s
-// CHECK-YC-I2: support for '/Yc' without a corresponding /FI flag not implemented yet; flag ignored
+// CHECK-YU-NOFI: cc1
+// CHECK-YU-NOFI: -emit-obj
+// CHECK-YU-NOFI: -include-pch
+// CHECK-YU-NOFI: foo.pch
+// CHECK-YU-NOFI: -pch-through-header=foo.h
+// CHECK-YU-NOFI: -x
+// CHECK-YU-NOFI: c++
+// CHECK-YU-NOFI: cl-pch.cpp
 
 // With an actual /I argument.
 // RUN: %clang_cl -Werror /Ifoo /Ycpchfile.h /FIpchfile.h /c -### -- %s 2>&1 \
@@ -226,20 +246,17 @@
 // CHECK-YC-I3: -include-pch
 // CHECK-YC-I3: pchfile.pch
 
-// Check that ./ isn't canonicalized away for /Yu either.
-// RUN: %clang_cl -Werror /Yupchfile.h /FI./pchfile.h /c -### -- %s 2>&1 \
-// RUN:   | FileCheck -check-prefix=CHECK-YU-I1 %s
-// CHECK-YU-I1: support for '/Yu' without a corresponding /FI flag not implemented yet; flag ignored
-
 // But /FIfoo/bar.h /Ycfoo\bar.h does work, as does /FIfOo.h /Ycfoo.H
-// FIXME: This part isn't implemented yet. The following two tests should not
-// show an error but do regular /Yu handling.
 // RUN: %clang_cl -Werror /YupchFILE.h /FI./pchfile.h /c -### -- %s 2>&1 \
 // RUN:   | FileCheck -check-prefix=CHECK-YU-CASE %s
-// CHECK-YU-CASE: support for '/Yu' without a corresponding /FI flag not implemented yet; flag ignored
-// RUN: %clang_cl -Werror /Yu./pchfile.h /FI.\pchfile.h /c -### -- %s 2>&1 \
+// CHECK-YU-CASE: -pch-through-header=pchFILE.h
+// CHECK-YU-CASE: -include
+// CHECK-YU-CASE: "./pchfile.h"
+// RUN: %clang_cl -Werror /Yu./pchfile.h /FI.\\pchfile.h /c -### -- %s 2>&1 \
 // RUN:   | FileCheck -check-prefix=CHECK-YU-SLASH %s
-// CHECK-YU-SLASH: support for '/Yu' without a corresponding /FI flag not implemented yet; flag ignored
+// CHECK-YU-SLASH: -pch-through-header=./pchfile.h
+// CHECK-YU-SLASH: -include
+// CHECK-YU-SLASH: ".{{[/\\]+}}pchfile.h"
 
 // cl.exe warns on multiple /Yc, /Yu, /Fp arguments, but clang-cl silently just
 // uses the last one.  This is true for e.g. /Fo too, so not warning on this
Index: test/Driver/cl-pch-search.cpp
===================================================================
--- test/Driver/cl-pch-search.cpp
+++ test/Driver/cl-pch-search.cpp
@@ -3,4 +3,4 @@
 
 // REQUIRES: x86-registered-target
 // Check that pchfile.h next to pchfile.cc is found correctly.
-// RUN: %clang_cl -Werror --target=x86_64-windows /Ycpchfile.h /FIpchfile.h /c /Fo%t.obj /Fp%t.pch -- %S/Inputs/pchfile.cpp
+// RUN: %clang_cl -Werror -Wno-microsoft-include --target=x86_64-windows /Ycpchfile.h /FIpchfile.h /c /Fo%t.obj /Fp%t.pch -- %S/Inputs/pchfile.cpp
Index: lib/Serialization/ASTReader.cpp
===================================================================
--- lib/Serialization/ASTReader.cpp
+++ lib/Serialization/ASTReader.cpp
@@ -704,6 +704,17 @@
   // Compute the #include and #include_macros lines we need.
   for (unsigned I = 0, N = ExistingPPOpts.Includes.size(); I != N; ++I) {
     StringRef File = ExistingPPOpts.Includes[I];
+
+    if (!ExistingPPOpts.ImplicitPCHInclude.empty() &&
+        !ExistingPPOpts.PCHThroughHeader.empty()) {
+      // In case the through header is an include, we must add all the includes
+      // to the predefines so the start point can be determined.
+      SuggestedPredefines += "#include \"";
+      SuggestedPredefines += File;
+      SuggestedPredefines += "\"\n";
+      continue;
+    }
+
     if (File == ExistingPPOpts.ImplicitPCHInclude)
       continue;
 
Index: lib/Parse/ParseAST.cpp
===================================================================
--- lib/Parse/ParseAST.cpp
+++ lib/Parse/ParseAST.cpp
@@ -141,6 +141,12 @@
     CleanupParser(ParseOP.get());
 
   S.getPreprocessor().EnterMainSourceFile();
+  if (!S.getPreprocessor().getCurrentLexer()) {
+    // If a PCH through header is specified that does not have an include in
+    // the source, there won't be any tokens or a Lexer.
+    return;
+  }
+
   P.Initialize();
 
   Parser::DeclGroupPtrTy ADecl;
Index: lib/Lex/Preprocessor.cpp
===================================================================
--- lib/Lex/Preprocessor.cpp
+++ lib/Lex/Preprocessor.cpp
@@ -149,6 +149,11 @@
     Ident_AbnormalTermination = nullptr;
   }
 
+  // If using a PCH with a through header, start skipping tokens.
+  if (!this->PPOpts->PCHThroughHeader.empty() &&
+      !this->PPOpts->ImplicitPCHInclude.empty())
+    SkippingUntilPCHThroughHeader = true;
+
   if (this->PPOpts->GeneratePreamble)
     PreambleConditionalStack.startRecording();
 }
@@ -551,6 +556,70 @@
 
   // Start parsing the predefines.
   EnterSourceFile(FID, nullptr, SourceLocation());
+
+  if (!PPOpts->PCHThroughHeader.empty()) {
+    // Lookup and save the FileID for the through header. If it isn't found
+    // in the search path, it's a fatal error.
+    const DirectoryLookup *CurDir;
+    const FileEntry *File = LookupFile(
+        SourceLocation(), PPOpts->PCHThroughHeader,
+        /*isAngled=*/false, /*FromDir=*/nullptr, /*FromFile=*/nullptr, CurDir,
+        /*SearchPath=*/nullptr, /*RelativePath=*/nullptr,
+        /*SuggestedModule=*/nullptr, /*IsMapped=*/nullptr);
+    if (!File) {
+      Diag(SourceLocation(), diag::err_pp_through_header_not_found)
+          << PPOpts->PCHThroughHeader;
+      return;
+    }
+    setPCHThroughHeaderFileID(
+        SourceMgr.createFileID(File, SourceLocation(), SrcMgr::C_User));
+  }
+
+  // Skip tokens from the Predefines and if needed the main file.
+  if (usingPCHWithThroughHeader() && SkippingUntilPCHThroughHeader)
+    SkipTokensUntilPCHThroughHeader();
+}
+
+/// Return true if the FileEntry is the PCH through header.
+bool Preprocessor::isPCHThroughHeader(const FileEntry *FE) {
+  assert(PCHThroughHeaderFileID.isValid() &&
+         "Invalid PCH through header FileID");
+  return FE == SourceMgr.getFileEntryForID(PCHThroughHeaderFileID);
+}
+
+/// Return true if creating a PCH with a through header.
+bool Preprocessor::creatingPCHWithThroughHeader() {
+  return TUKind == TU_Prefix && !PPOpts->PCHThroughHeader.empty() &&
+         PCHThroughHeaderFileID.isValid();
+}
+
+/// Return true if using a PCH with a through header.
+bool Preprocessor::usingPCHWithThroughHeader() {
+  return TUKind != TU_Prefix && !PPOpts->PCHThroughHeader.empty() &&
+         PCHThroughHeaderFileID.isValid();
+}
+
+/// Skip tokens until after the #include of the through header.
+/// Tokens in the predefines file and the main file may be skipped. If the end
+/// of the predefines file is reached, skipping continues into the main file.
+/// If the end of the main file is reached, it's a fatal error.
+void Preprocessor::SkipTokensUntilPCHThroughHeader()
+{
+  bool ReachedMainFileEOF = false;
+  Token Tok;
+  while (true) {
+    bool InPredefines = (CurLexer->getFileID() == getPredefinesFileID());
+    CurLexer->Lex(Tok);
+    if (Tok.is(tok::eof) && !InPredefines) {
+      ReachedMainFileEOF = true;
+      break;
+    }
+    if (!SkippingUntilPCHThroughHeader)
+      break;
+  }
+  if (ReachedMainFileEOF)
+    Diag(SourceLocation(), diag::err_pp_through_header_not_seen)
+        << PPOpts->PCHThroughHeader << 1;
 }
 
 void Preprocessor::replayPreambleConditionalStack() {
Index: lib/Lex/PPLexerChange.cpp
===================================================================
--- lib/Lex/PPLexerChange.cpp
+++ lib/Lex/PPLexerChange.cpp
@@ -13,6 +13,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/Lex/Preprocessor.h"
+#include "clang/Lex/PreprocessorOptions.h"
 #include "clang/Basic/FileManager.h"
 #include "clang/Basic/SourceManager.h"
 #include "clang/Lex/HeaderSearch.h"
@@ -340,6 +341,7 @@
   // module, complain and close it now.
   // FIXME: This is not correct if we are building a module from PTH.
   const bool LeavingSubmodule = CurLexer && CurLexerSubmodule;
+  bool LeavingPCHThroughHeader = false;
   if ((LeavingSubmodule || IncludeMacroStack.empty()) &&
       !BuildingSubmoduleStack.empty() &&
       BuildingSubmoduleStack.back().IsPragma) {
@@ -481,6 +483,12 @@
       Result.setAnnotationValue(M);
     }
 
+    bool FoundPCHThroughHeader = false;
+    if (CurPPLexer && creatingPCHWithThroughHeader() &&
+        isPCHThroughHeader(
+            SourceMgr.getFileEntryForID(CurPPLexer->getFileID())))
+      FoundPCHThroughHeader = true;
+
     // We're done with the #included file.
     RemoveTopOfLexerStack();
 
@@ -500,8 +508,16 @@
     if (ExitedFromPredefinesFile)
       replayPreambleConditionalStack();
 
-    // Client should lex another token unless we generated an EOM.
-    return LeavingSubmodule;
+    if (!isEndOfMacro && CurPPLexer && FoundPCHThroughHeader &&
+        (isInPrimaryFile() ||
+         CurPPLexer->getFileID() == getPredefinesFileID())) {
+      // Leaving the through header. Continue directly to end of main file
+      // processing.
+      LeavingPCHThroughHeader = true;
+    } else {
+      // Client should lex another token unless we generated an EOM.
+      return LeavingSubmodule;
+    }
   }
 
   // If this is the end of the main file, form an EOF token.
@@ -522,6 +538,12 @@
         Result.setLocation(Result.getLocation().getLocWithOffset(-1));
     }
 
+    if (creatingPCHWithThroughHeader() && !LeavingPCHThroughHeader) {
+      // Reached the end of the compilation without finding the through header.
+      Diag(CurLexer->getFileLoc(), diag::err_pp_through_header_not_seen)
+          << PPOpts->PCHThroughHeader << 0;
+    }
+
     if (!isIncrementalProcessingEnabled())
       // We're done with lexing.
       CurLexer.reset();
Index: lib/Lex/PPDirectives.cpp
===================================================================
--- lib/Lex/PPDirectives.cpp
+++ lib/Lex/PPDirectives.cpp
@@ -1862,6 +1862,12 @@
     }
   }
 
+  if (usingPCHWithThroughHeader() && SkippingUntilPCHThroughHeader) {
+    if (isPCHThroughHeader(File))
+      SkippingUntilPCHThroughHeader = false;
+    return;
+  }
+
   // Should we enter the source file? Set to false if either the source file is
   // known to have no effect beyond its effect on module visibility -- that is,
   // if it's got an include guard that is already defined or is a modular header
Index: lib/Frontend/CompilerInvocation.cpp
===================================================================
--- lib/Frontend/CompilerInvocation.cpp
+++ lib/Frontend/CompilerInvocation.cpp
@@ -1541,7 +1541,6 @@
     = Args.getLastArgValue(OPT_foverride_record_layout_EQ);
   Opts.AuxTriple =
       llvm::Triple::normalize(Args.getLastArgValue(OPT_aux_triple));
-  Opts.FindPchSource = Args.getLastArgValue(OPT_find_pch_source_EQ);
   Opts.StatsFile = Args.getLastArgValue(OPT_stats_file);
 
   if (const Arg *A = Args.getLastArg(OPT_arcmt_check,
@@ -2800,6 +2799,7 @@
                                   frontend::ActionKind Action) {
   Opts.ImplicitPCHInclude = Args.getLastArgValue(OPT_include_pch);
   Opts.ImplicitPTHInclude = Args.getLastArgValue(OPT_include_pth);
+  Opts.PCHThroughHeader = Args.getLastArgValue(OPT_pch_through_header_EQ);
   if (const Arg *A = Args.getLastArg(OPT_token_cache))
       Opts.TokenCache = A->getValue();
   else
Index: lib/Frontend/CompilerInstance.cpp
===================================================================
--- lib/Frontend/CompilerInstance.cpp
+++ lib/Frontend/CompilerInstance.cpp
@@ -852,36 +852,7 @@
 
   // Figure out where to get and map in the main file.
   if (InputFile != "-") {
-    const FileEntry *File;
-    if (Opts.FindPchSource.empty()) {
-      File = FileMgr.getFile(InputFile, /*OpenFile=*/true);
-    } else {
-      // When building a pch file in clang-cl mode, the .h file is built as if
-      // it was included by a cc file.  Since the driver doesn't know about
-      // all include search directories, the frontend must search the input
-      // file through HeaderSearch here, as if it had been included by the
-      // cc file at Opts.FindPchSource.
-      const FileEntry *FindFile = FileMgr.getFile(Opts.FindPchSource);
-      if (!FindFile) {
-        Diags.Report(diag::err_fe_error_reading) << Opts.FindPchSource;
-        return false;
-      }
-      const DirectoryLookup *UnusedCurDir;
-      SmallVector<std::pair<const FileEntry *, const DirectoryEntry *>, 16>
-          Includers;
-      Includers.push_back(std::make_pair(FindFile, FindFile->getDir()));
-      File = HS->LookupFile(InputFile, SourceLocation(), /*isAngled=*/false,
-                            /*FromDir=*/nullptr,
-                            /*CurDir=*/UnusedCurDir, Includers,
-                            /*SearchPath=*/nullptr,
-                            /*RelativePath=*/nullptr,
-                            /*RequestingModule=*/nullptr,
-                            /*SuggestedModule=*/nullptr, /*IsMapped=*/nullptr,
-                            /*SkipCache=*/true);
-      // Also add the header to /showIncludes output.
-      if (File)
-        DepOpts.ShowIncludesPretendHeader = File->getName();
-    }
+    const FileEntry *File = FileMgr.getFile(InputFile, /*OpenFile=*/true);
     if (!File) {
       Diags.Report(diag::err_fe_error_reading) << InputFile;
       return false;
Index: lib/Driver/ToolChains/Clang.cpp
===================================================================
--- lib/Driver/ToolChains/Clang.cpp
+++ lib/Driver/ToolChains/Clang.cpp
@@ -1076,73 +1076,26 @@
   // wonky, but we include looking for .gch so we can support seamless
   // replacement into a build system already set up to be generating
   // .gch files.
-  int YcIndex = -1, YuIndex = -1;
-  {
-    int AI = -1;
+
+  if (getToolChain().getDriver().IsCLMode()) {
     const Arg *YcArg = Args.getLastArg(options::OPT__SLASH_Yc);
     const Arg *YuArg = Args.getLastArg(options::OPT__SLASH_Yu);
-    for (const Arg *A : Args.filtered(options::OPT_clang_i_Group)) {
-      // Walk the whole i_Group and skip non "-include" flags so that the index
-      // here matches the index in the next loop below.
-      ++AI;
-      if (!A->getOption().matches(options::OPT_include))
-        continue;
-      if (YcArg && strcmp(A->getValue(), YcArg->getValue()) == 0)
-        YcIndex = AI;
-      if (YuArg && strcmp(A->getValue(), YuArg->getValue()) == 0)
-        YuIndex = AI;
+    if (YcArg || YuArg) {
+      StringRef ThroughHeader = YcArg ? YcArg->getValue() : YuArg->getValue();
+      if (!isa<PrecompileJobAction>(JA)) {
+        CmdArgs.push_back("-include-pch");
+        CmdArgs.push_back(Args.MakeArgString(D.GetClPchPath(C, ThroughHeader)));
+      }
+      CmdArgs.push_back(
+          Args.MakeArgString(Twine("-pch-through-header=") + ThroughHeader));
     }
   }
-  if (isa<PrecompileJobAction>(JA) && YcIndex != -1) {
-    Driver::InputList Inputs;
-    D.BuildInputs(getToolChain(), C.getArgs(), Inputs);
-    assert(Inputs.size() == 1 && "Need one input when building pch");
-    CmdArgs.push_back(Args.MakeArgString(Twine("-find-pch-source=") +
-                                         Inputs[0].second->getValue()));
-  }
 
   bool RenderedImplicitInclude = false;
   int AI = -1;
   for (const Arg *A : Args.filtered(options::OPT_clang_i_Group)) {
     ++AI;
-
-    if (getToolChain().getDriver().IsCLMode() &&
-        A->getOption().matches(options::OPT_include)) {
-      // In clang-cl mode, /Ycfoo.h means that all code up to a foo.h
-      // include is compiled into foo.h, and everything after goes into
-      // the .obj file. /Yufoo.h means that all includes prior to and including
-      // foo.h are completely skipped and replaced with a use of the pch file
-      // for foo.h.  (Each flag can have at most one value, multiple /Yc flags
-      // just mean that the last one wins.)  If /Yc and /Yu are both present
-      // and refer to the same file, /Yc wins.
-      // Note that OPT__SLASH_FI gets mapped to OPT_include.
-      // FIXME: The code here assumes that /Yc and /Yu refer to the same file.
-      // cl.exe seems to support both flags with different values, but that
-      // seems strange (which flag does /Fp now refer to?), so don't implement
-      // that until someone needs it.
-      int PchIndex = YcIndex != -1 ? YcIndex : YuIndex;
-      if (PchIndex != -1) {
-        if (isa<PrecompileJobAction>(JA)) {
-          // When building the pch, skip all includes after the pch.
-          assert(YcIndex != -1 && PchIndex == YcIndex);
-          if (AI >= YcIndex)
-            continue;
-        } else {
-          // When using the pch, skip all includes prior to the pch.
-          if (AI < PchIndex) {
-            A->claim();
-            continue;
-          }
-          if (AI == PchIndex) {
-            A->claim();
-            CmdArgs.push_back("-include-pch");
-            CmdArgs.push_back(
-                Args.MakeArgString(D.GetClPchPath(C, A->getValue())));
-            continue;
-          }
-        }
-      }
-    } else if (A->getOption().matches(options::OPT_include)) {
+    if (A->getOption().matches(options::OPT_include)) {
       // Handling of gcc-style gch precompiled headers.
       bool IsFirstImplicitInclude = !RenderedImplicitInclude;
       RenderedImplicitInclude = true;
Index: lib/Driver/Driver.cpp
===================================================================
--- lib/Driver/Driver.cpp
+++ lib/Driver/Driver.cpp
@@ -2859,22 +2859,6 @@
     Args.eraseArg(options::OPT__SLASH_Yu);
     YcArg = YuArg = nullptr;
   }
-  if (YcArg || YuArg) {
-    StringRef Val = YcArg ? YcArg->getValue() : YuArg->getValue();
-    bool FoundMatchingInclude = false;
-    for (const Arg *Inc : Args.filtered(options::OPT_include)) {
-      // FIXME: Do case-insensitive matching and consider / and \ as equal.
-      if (Inc->getValue() == Val)
-        FoundMatchingInclude = true;
-    }
-    if (!FoundMatchingInclude) {
-      Diag(clang::diag::warn_drv_ycyu_no_fi_arg_clang_cl)
-          << (YcArg ? YcArg : YuArg)->getSpelling();
-      Args.eraseArg(options::OPT__SLASH_Yc);
-      Args.eraseArg(options::OPT__SLASH_Yu);
-      YcArg = YuArg = nullptr;
-    }
-  }
   if (YcArg && Inputs.size() > 1) {
     Diag(clang::diag::warn_drv_yc_multiple_inputs_clang_cl);
     Args.eraseArg(options::OPT__SLASH_Yc);
@@ -2944,11 +2928,9 @@
         const types::ID HeaderType = lookupHeaderTypeForSourceType(InputType);
         llvm::SmallVector<phases::ID, phases::MaxNumberOfPhases> PCHPL;
         types::getCompilationPhases(HeaderType, PCHPL);
-        Arg *PchInputArg = MakeInputArg(Args, *Opts, YcArg->getValue());
-
         // Build the pipeline for the pch file.
         Action *ClangClPch =
-            C.MakeAction<InputAction>(*PchInputArg, HeaderType);
+            C.MakeAction<InputAction>(*InputArg, HeaderType);
         for (phases::ID Phase : PCHPL)
           ClangClPch = ConstructPhaseAction(C, Args, Phase, ClangClPch);
         assert(ClangClPch);
@@ -4124,6 +4106,9 @@
     // extension of .pch is assumed. "
     if (!llvm::sys::path::has_extension(Output))
       Output += ".pch";
+  } else if (Arg *YcArg = C.getArgs().getLastArg(options::OPT__SLASH_Yc)) {
+    Output = YcArg->getValue();
+    llvm::sys::path::replace_extension(Output, ".pch");
   } else {
     Output = BaseName;
     llvm::sys::path::replace_extension(Output, ".pch");
Index: include/clang/Lex/PreprocessorOptions.h
===================================================================
--- include/clang/Lex/PreprocessorOptions.h
+++ include/clang/Lex/PreprocessorOptions.h
@@ -54,6 +54,9 @@
   /// definitions and expansions.
   bool DetailedRecord = false;
 
+  /// If non-empty, is the start/stop point for a precompiled header.
+  std::string PCHThroughHeader;
+
   /// The implicit PCH included at the start of the translation unit, or empty.
   std::string ImplicitPCHInclude;
 
Index: include/clang/Lex/Preprocessor.h
===================================================================
--- include/clang/Lex/Preprocessor.h
+++ include/clang/Lex/Preprocessor.h
@@ -720,6 +720,12 @@
   /// The file ID for the preprocessor predefines.
   FileID PredefinesFileID;
 
+  /// The file ID for the PCH through header.
+  FileID PCHThroughHeaderFileID;
+
+  /// Whether tokens are being skipped until the through header is seen.
+  bool SkippingUntilPCHThroughHeader = false;
+
   /// \{
   /// Cache of macro expanders to reduce malloc traffic.
   enum { TokenLexerCacheSize = 8 };
@@ -1140,6 +1146,18 @@
   /// all macro expansions, macro definitions, etc.
   void createPreprocessingRecord();
 
+  /// Returns true if the FileEntry is the PCH through header.
+  bool isPCHThroughHeader(const FileEntry *File);
+
+  /// True if creating a PCH with a through header.
+  bool creatingPCHWithThroughHeader();
+
+  /// True if using a PCH with a through header.
+  bool usingPCHWithThroughHeader();
+
+  /// Skip tokens until after the #include of the through header.
+  void SkipTokensUntilPCHThroughHeader();
+
   /// Enter the specified FileID as the main source file,
   /// which implicitly adds the builtin defines etc.
   void EnterMainSourceFile();
@@ -2020,6 +2038,13 @@
     PredefinesFileID = FID;
   }
 
+  /// Set the FileID for the PCH through header.
+  void setPCHThroughHeaderFileID(FileID FID) {
+    assert(PCHThroughHeaderFileID.isInvalid() &&
+           "PCHThroughHeaderFileID already set!");
+    PCHThroughHeaderFileID = FID;
+  }
+
   /// Returns true if we are lexing from a file and not a
   /// pragma or a macro.
   static bool IsFileLexer(const Lexer* L, const PreprocessorLexer* P) {
Index: include/clang/Frontend/FrontendOptions.h
===================================================================
--- include/clang/Frontend/FrontendOptions.h
+++ include/clang/Frontend/FrontendOptions.h
@@ -432,10 +432,6 @@
   /// Auxiliary triple for CUDA compilation.
   std::string AuxTriple;
 
-  /// If non-empty, search the pch input file as if it was a header
-  /// included by this file.
-  std::string FindPchSource;
-
   /// Filename to write statistics to.
   std::string StatsFile;
 
Index: include/clang/Driver/CC1Options.td
===================================================================
--- include/clang/Driver/CC1Options.td
+++ include/clang/Driver/CC1Options.td
@@ -599,10 +599,9 @@
 
 def foverride_record_layout_EQ : Joined<["-"], "foverride-record-layout=">,
   HelpText<"Override record layouts with those in the given file">;
-def find_pch_source_EQ : Joined<["-"], "find-pch-source=">,
-  HelpText<"When building a pch, try to find the input file in include "
-           "directories, as if it had been included by the argument passed "
-           "to this flag.">;
+def pch_through_header_EQ : Joined<["-"], "pch-through-header=">,
+  HelpText<"When creating a pch stop at this file.  When using a pch start "
+           "after this file.">;
 def fno_pch_timestamp : Flag<["-"], "fno-pch-timestamp">,
   HelpText<"Disable inclusion of timestamp in precompiled headers">;
   
Index: include/clang/Basic/DiagnosticLexKinds.td
===================================================================
--- include/clang/Basic/DiagnosticLexKinds.td
+++ include/clang/Basic/DiagnosticLexKinds.td
@@ -404,6 +404,11 @@
 def err_pp_directive_required : Error<
   "%0 must be used within a preprocessing directive">;
 def err_pp_file_not_found : Error<"'%0' file not found">, DefaultFatal;
+def err_pp_through_header_not_found : Error<
+  "'%0' required for precompiled header not found">, DefaultFatal;
+def err_pp_through_header_not_seen : Error<
+  "#include of '%0' not seen while attempting to "
+  "%select{create|use}1 precompiled header">, DefaultFatal;
 def err_pp_file_not_found_not_fatal : Error<
   "'%0' file not found with <angled> include; use \"quotes\" instead">;
 def err_pp_error_opening_file : Error<
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to