Index: include/clang/Lex/Preprocessor.h =================================================================== --- include/clang/Lex/Preprocessor.h +++ include/clang/Lex/Preprocessor.h @@ -281,6 +281,60 @@ /// This is used when loading a precompiled preamble. std::pair SkipMainFilePreamble; + class PreambleConditionalStackStore { + enum State { + Off = 0, + Recording = 1, + Replaying = 2, + }; + + void setState(State s) { + ConditionalStackState = s; + } + + public: + typedef SmallVector Stack; + + PreambleConditionalStackStore() + : ConditionalStack(nullptr) + , ConditionalStackState(Off) + {} + ~PreambleConditionalStackStore() + { delete ConditionalStack; } + + void startRecording() { ConditionalStackState = Recording; } + void startReplaying() { ConditionalStackState = Replaying; } + bool isRecording() const { return ConditionalStackState == Recording; } + bool isReplaying() const { return ConditionalStackState == Replaying; } + + const Stack &getStack() const { + assert(ConditionalStack); + return *ConditionalStack; + } + + void doneReplaying() { + assert(ConditionalStack); + delete ConditionalStack; + ConditionalStack = nullptr; + setState(Off); + } + + void setStack(const Stack &s) { + if (!isRecording() && !isReplaying()) + return; + if (ConditionalStack) + *ConditionalStack = s; + else + ConditionalStack = new Stack(s); + } + + bool hasRecordedPreamble() const { return ConditionalStack; } + + private: + Stack *ConditionalStack; + State ConditionalStackState; + } PreambleConditionalStack; + /// \brief The current top of the stack that we're lexing from if /// not expanding a macro and we are lexing directly from source code. /// @@ -1676,6 +1730,11 @@ /// \brief Return true if we're in the top-level file, not in a \#include. bool isInPrimaryFile() const; + /// \brief Return true if we're in the 1-level deep \#include. This is used + /// by the lexer to determine if it needs to generate errors about + /// unterminated \#if directives. + bool isInPreamble() const; + /// \brief Handle cases where the \#include name is expanded /// from a macro as multiple tokens, which need to be glued together. /// @@ -1918,6 +1977,28 @@ const FileEntry *getModuleHeaderToIncludeForDiagnostics(SourceLocation IncLoc, SourceLocation MLoc); + bool isRecordingPreamble() const { + return PreambleConditionalStack.isRecording(); + } + + bool hasRecordedPreamble() const { + return PreambleConditionalStack.hasRecordedPreamble(); + } + + const SmallVector &getPreambleConditionalStack() const + { return PreambleConditionalStack.getStack(); } + + void setRecordedPreambleConditionalStack( + const SmallVector &s) { + PreambleConditionalStack.setStack(s); + } + + void setReplayablePreambleConditionalStack( + const SmallVector &s) { + PreambleConditionalStack.startReplaying(); + PreambleConditionalStack.setStack(s); + } + private: // Macro handling. void HandleDefineDirective(Token &Tok, bool ImmediatelyAfterTopLevelIfndef); Index: include/clang/Lex/PreprocessorLexer.h =================================================================== --- include/clang/Lex/PreprocessorLexer.h +++ include/clang/Lex/PreprocessorLexer.h @@ -176,6 +176,9 @@ conditional_iterator conditional_end() const { return ConditionalStack.end(); } + + void setConditionalLevels(const SmallVector &CL) + { ConditionalStack = CL; } }; } // end namespace clang Index: include/clang/Lex/PreprocessorOptions.h =================================================================== --- include/clang/Lex/PreprocessorOptions.h +++ include/clang/Lex/PreprocessorOptions.h @@ -80,6 +80,8 @@ /// The boolean indicates whether the preamble ends at the start of a new /// line. std::pair PrecompiledPreambleBytes; + + bool PreambleGeneration = false; /// The implicit PTH input included at the start of the translation unit, or /// empty. Index: include/clang/Serialization/ASTBitCodes.h =================================================================== --- include/clang/Serialization/ASTBitCodes.h +++ include/clang/Serialization/ASTBitCodes.h @@ -585,6 +585,9 @@ /// \brief Number of unmatched #pragma clang cuda_force_host_device begin /// directives we've seen. CUDA_PRAGMA_FORCE_HOST_DEVICE_DEPTH = 57, + + /// \brief The stack of open #ifs/#ifdefs recorded in a preamble. + PP_CONDITIONAL_STACK = 58, }; /// \brief Record types used within a source manager block. Index: lib/Frontend/ASTUnit.cpp =================================================================== --- lib/Frontend/ASTUnit.cpp +++ lib/Frontend/ASTUnit.cpp @@ -1973,6 +1973,7 @@ PreprocessorOptions &PPOpts = CI->getPreprocessorOpts(); PPOpts.RemappedFilesKeepOriginalName = RemappedFilesKeepOriginalName; PPOpts.AllowPCHWithCompilerErrors = AllowPCHWithCompilerErrors; + PPOpts.PreambleGeneration = PrecompilePreambleAfterNParses != 0; // Override the resources path. CI->getHeaderSearchOpts().ResourceDir = ResourceFilesPath; Index: lib/Lex/Lexer.cpp =================================================================== --- lib/Lex/Lexer.cpp +++ lib/Lex/Lexer.cpp @@ -535,8 +535,6 @@ enum PreambleDirectiveKind { PDK_Skipped, - PDK_StartIf, - PDK_EndIf, PDK_Unknown }; @@ -559,8 +557,6 @@ bool InPreprocessorDirective = false; Token TheTok; - Token IfStartTok; - unsigned IfCount = 0; SourceLocation ActiveCommentLoc; unsigned MaxLineOffset = 0; @@ -643,33 +639,18 @@ .Case("sccs", PDK_Skipped) .Case("assert", PDK_Skipped) .Case("unassert", PDK_Skipped) - .Case("if", PDK_StartIf) - .Case("ifdef", PDK_StartIf) - .Case("ifndef", PDK_StartIf) + .Case("if", PDK_Skipped) + .Case("ifdef", PDK_Skipped) + .Case("ifndef", PDK_Skipped) .Case("elif", PDK_Skipped) .Case("else", PDK_Skipped) - .Case("endif", PDK_EndIf) + .Case("endif", PDK_Skipped) .Default(PDK_Unknown); switch (PDK) { case PDK_Skipped: continue; - case PDK_StartIf: - if (IfCount == 0) - IfStartTok = HashTok; - - ++IfCount; - continue; - - case PDK_EndIf: - // Mismatched #endif. The preamble ends here. - if (IfCount == 0) - break; - - --IfCount; - continue; - case PDK_Unknown: // We don't know what this directive is; stop at the '#'. break; @@ -690,16 +671,13 @@ } while (true); SourceLocation End; - if (IfCount) - End = IfStartTok.getLocation(); - else if (ActiveCommentLoc.isValid()) + if (ActiveCommentLoc.isValid()) End = ActiveCommentLoc; // don't truncate a decl comment. else End = TheTok.getLocation(); return std::make_pair(End.getRawEncoding() - StartLoc.getRawEncoding(), - IfCount? IfStartTok.isAtStartOfLine() - : TheTok.isAtStartOfLine()); + TheTok.isAtStartOfLine()); } /// AdvanceToTokenCharacter - Given a location that specifies the start of a @@ -2540,6 +2518,12 @@ return true; } + if (PP->isRecordingPreamble()) { + PP->setRecordedPreambleConditionalStack(ConditionalStack); + if (PP->isInPreamble()) + ConditionalStack.clear(); + } + // Issue diagnostics for unterminated #if and missing newline. // If we are in a #if directive, emit an error. Index: lib/Lex/PPLexerChange.cpp =================================================================== --- lib/Lex/PPLexerChange.cpp +++ lib/Lex/PPLexerChange.cpp @@ -46,6 +46,12 @@ }); } +bool Preprocessor::isInPreamble() const { + if (IsFileLexer()) + return IncludeMacroStack.size() == 1; + return false; +} + /// getCurrentLexer - Return the current file lexer being lexed from. Note /// that this ignores any potentially active macro expansions and _Pragma /// expansions going on at the time. @@ -129,6 +135,11 @@ Callbacks->FileChanged(CurLexer->getFileLoc(), PPCallbacks::EnterFile, FileType); } + + if (PreambleConditionalStack.isReplaying()) { + CurPPLexer->setConditionalLevels(PreambleConditionalStack.getStack()); + PreambleConditionalStack.doneReplaying(); + } } /// EnterSourceFileWithPTH - Add a source file to the top of the include stack Index: lib/Lex/Preprocessor.cpp =================================================================== --- lib/Lex/Preprocessor.cpp +++ lib/Lex/Preprocessor.cpp @@ -150,6 +150,9 @@ Ident_GetExceptionInfo = Ident_GetExceptionCode = nullptr; Ident_AbnormalTermination = nullptr; } + + if (this->PPOpts->PreambleGeneration) + PreambleConditionalStack.startRecording(); } Preprocessor::~Preprocessor() { Index: lib/Serialization/ASTReader.cpp =================================================================== --- lib/Serialization/ASTReader.cpp +++ lib/Serialization/ASTReader.cpp @@ -2870,6 +2870,20 @@ } break; + case PP_CONDITIONAL_STACK: + if (!Record.empty()) { + SmallVector ConditionalStack; + for (unsigned Idx = 0, N = Record.size() - 1; Idx < N; /* in loop */) { + auto loc = ReadSourceLocation(F, Record, Idx); + bool WasSkipping = Record[Idx++]; + bool FoundNonSkip = Record[Idx++]; + bool FoundElse = Record[Idx++]; + ConditionalStack.push_back({ loc, WasSkipping, FoundNonSkip, FoundElse }); + } + PP.setReplayablePreambleConditionalStack(ConditionalStack); + } + break; + case PP_COUNTER_VALUE: if (!Record.empty() && Listener) Listener->ReadCounter(F, Record[0]); Index: lib/Serialization/ASTWriter.cpp =================================================================== --- lib/Serialization/ASTWriter.cpp +++ lib/Serialization/ASTWriter.cpp @@ -1071,6 +1071,7 @@ RECORD(UNUSED_LOCAL_TYPEDEF_NAME_CANDIDATES); RECORD(DELETE_EXPRS_TO_ANALYZE); RECORD(CUDA_PRAGMA_FORCE_HOST_DEVICE_DEPTH); + RECORD(PP_CONDITIONAL_STACK); // SourceManager Block. BLOCK(SOURCE_MANAGER_BLOCK); @@ -2233,6 +2234,17 @@ Stream.EmitRecord(PP_COUNTER_VALUE, Record); } + if (PP.isRecordingPreamble() && PP.hasRecordedPreamble()) { + for (const auto &Cond : PP.getPreambleConditionalStack()) { + AddSourceLocation(Cond.IfLoc, Record); + Record.push_back(Cond.WasSkipping); + Record.push_back(Cond.FoundNonSkip); + Record.push_back(Cond.FoundElse); + } + Stream.EmitRecord(PP_CONDITIONAL_STACK, Record); + Record.clear(); + } + // Enter the preprocessor block. Stream.EnterSubblock(PREPROCESSOR_BLOCK_ID, 3); Index: test/Lexer/preamble.c =================================================================== --- test/Lexer/preamble.c +++ test/Lexer/preamble.c @@ -9,15 +9,12 @@ #pragma unknown #endif #ifdef WIBBLE -#include "honk" -#else -int foo(); +#include "foo" +int bar; #endif // This test checks for detection of the preamble of a file, which -// includes all of the starting comments and #includes. Note that any -// changes to the preamble part of this file must be mirrored in -// Inputs/preamble.txt, since we diff against it. +// includes all of the starting comments and #includes. // RUN: %clang_cc1 -print-preamble %s > %t // RUN: echo END. >> %t @@ -33,4 +30,6 @@ // CHECK-NEXT: #endif // CHECK-NEXT: #pragma unknown // CHECK-NEXT: #endif +// CHECK-NEXT: #ifdef WIBBLE +// CHECK-NEXT: #include "foo" // CHECK-NEXT: END. Index: test/Lexer/preamble2.c =================================================================== --- /dev/null +++ test/Lexer/preamble2.c @@ -0,0 +1,19 @@ +// Preamble detection test: header with an include guard. +#ifndef HEADER_H +#define HEADER_H +#include "foo" +int bar; +#endif + +// This test checks for detection of the preamble of a file, which +// includes all of the starting comments and #includes. + +// RUN: %clang_cc1 -print-preamble %s > %t +// RUN: echo END. >> %t +// RUN: FileCheck < %t %s + +// CHECK: // Preamble detection test: header with an include guard. +// CHECK-NEXT: #ifndef HEADER_H +// CHECK-NEXT: #define HEADER_H +// CHECK-NEXT: #include "foo" +// CHECK-NEXT: END.