diff --git a/include/clang/Driver/Options.td b/include/clang/Driver/Options.td index 6b7e198d0bf3897c1fa5f626da50d2e5399e95d6..731bb0406b36fa878ca358bd6839183a6d1acc4c 100644 --- a/include/clang/Driver/Options.td +++ b/include/clang/Driver/Options.td @@ -927,6 +927,8 @@ def include_pch : Separate<["-"], "include-pch">, Group<clang_i_Group>, Flags<[C HelpText<"Include precompiled header file">, MetaVarName<"<file>">; def relocatable_pch : Flag<["-", "--"], "relocatable-pch">, Flags<[CC1Option]>, HelpText<"Whether to build a relocatable precompiled header">; +def verify_pch : Flag<["-"], "verify-pch">, Group<Action_Group>, Flags<[CC1Option]>, + HelpText<"Load and verify that a pre-compiled header file is not stale">; def init : Separate<["-"], "init">; def install__name : Separate<["-"], "install_name">; def integrated_as : Flag<["-"], "integrated-as">, Flags<[DriverOption]>; diff --git a/include/clang/Frontend/CompilerInstance.h b/include/clang/Frontend/CompilerInstance.h index fa20ae48e4f099fb282adcb7a700762428251642..64de7a5b197170564f3fe7bd3a93188a1f4b0fec 100644 --- a/include/clang/Frontend/CompilerInstance.h +++ b/include/clang/Frontend/CompilerInstance.h @@ -545,6 +545,7 @@ public: void createPCHExternalASTSource(StringRef Path, bool DisablePCHValidation, bool AllowPCHWithCompilerErrors, + bool AllowConfigurationMismatch, void *DeserializationListener); /// Create an external AST source to read a PCH file. @@ -554,6 +555,7 @@ public: createPCHExternalASTSource(StringRef Path, const std::string &Sysroot, bool DisablePCHValidation, bool AllowPCHWithCompilerErrors, + bool AllowConfigurationMismatch, Preprocessor &PP, ASTContext &Context, void *DeserializationListener, bool Preamble, bool UseGlobalModuleIndex); diff --git a/include/clang/Frontend/FrontendActions.h b/include/clang/Frontend/FrontendActions.h index f3d12769f1438baaeb1c990a14939534f47441cf..f5557888ad6a77f1fd8e759442ac4e28869de06b 100644 --- a/include/clang/Frontend/FrontendActions.h +++ b/include/clang/Frontend/FrontendActions.h @@ -146,6 +146,17 @@ public: virtual bool hasCodeCompletionSupport() const { return false; } }; +class VerifyPCHAction : public ASTFrontendAction { +protected: + virtual ASTConsumer *CreateASTConsumer(CompilerInstance &CI, + StringRef InFile); + + virtual void ExecuteAction(); + +public: + virtual bool hasCodeCompletionSupport() const { return false; } +}; + /** * \brief Frontend action adaptor that merges ASTs together. * diff --git a/include/clang/Frontend/FrontendOptions.h b/include/clang/Frontend/FrontendOptions.h index 818427a4ef6592b707bcf37eca8d0a2a75bacb2a..fc13b193936b1173fc2312dc9cefa1736e44b455 100644 --- a/include/clang/Frontend/FrontendOptions.h +++ b/include/clang/Frontend/FrontendOptions.h @@ -43,6 +43,7 @@ namespace frontend { GeneratePTH, ///< Generate pre-tokenized header. InitOnly, ///< Only execute frontend initialization. ModuleFileInfo, ///< Dump information about a module file. + VerifyPCH, ///< Load and verify that a PCH file is usable. ParseSyntaxOnly, ///< Parse and perform semantic analysis. PluginAction, ///< Run a plugin action, \see ActionName. PrintDeclContext, ///< Print DeclContext and their Decls. diff --git a/include/clang/Serialization/ASTReader.h b/include/clang/Serialization/ASTReader.h index 3cfd17ca027ae9bf7378e2c363941cc1e0f82fa3..00ab7f04926359f007a98d5739b68231fa85e733 100644 --- a/include/clang/Serialization/ASTReader.h +++ b/include/clang/Serialization/ASTReader.h @@ -733,6 +733,10 @@ private: /// \brief Whether to accept an AST file with compiler errors. bool AllowASTWithCompilerErrors; + /// \brief Whether to accept an AST file that has a different configuration + /// from the current compiler instance. + bool AllowConfigurationMismatch; + /// \brief Whether we are allowed to use the global module index. bool UseGlobalIndex; @@ -1174,11 +1178,15 @@ public: /// AST file the was created out of an AST with compiler errors, /// otherwise it will reject it. /// + /// \param AllowConfigurationMismatch If true, the AST reader will not check + /// for configuration differences between the AST file and the invocation. + /// /// \param UseGlobalIndex If true, the AST reader will try to load and use /// the global module index. ASTReader(Preprocessor &PP, ASTContext &Context, StringRef isysroot = "", bool DisableValidation = false, bool AllowASTWithCompilerErrors = false, + bool AllowConfigurationMismatch = false, bool UseGlobalIndex = true); ~ASTReader(); diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index bd43fbeec19e485ac497b55f162948cab4530007..270c3a701b03f8e0197cde4577e62fdecdbe473d 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -169,6 +169,7 @@ const { // -{fsyntax-only,-analyze,emit-ast,S} only run up to the compiler. } else if ((PhaseArg = DAL.getLastArg(options::OPT_fsyntax_only)) || (PhaseArg = DAL.getLastArg(options::OPT_module_file_info)) || + (PhaseArg = DAL.getLastArg(options::OPT_verify_pch)) || (PhaseArg = DAL.getLastArg(options::OPT_rewrite_objc)) || (PhaseArg = DAL.getLastArg(options::OPT_rewrite_legacy_objc)) || (PhaseArg = DAL.getLastArg(options::OPT__migrate)) || @@ -1316,6 +1317,8 @@ Action *Driver::ConstructPhaseAction(const ArgList &Args, phases::ID Phase, return new CompileJobAction(Input, types::TY_AST); } else if (Args.hasArg(options::OPT_module_file_info)) { return new CompileJobAction(Input, types::TY_ModuleFile); + } else if (Args.hasArg(options::OPT_verify_pch)) { + return new CompileJobAction(Input, types::TY_Nothing); } else if (IsUsingLTO(Args)) { types::ID Output = Args.hasArg(options::OPT_S) ? types::TY_LTO_IR : types::TY_LTO_BC; diff --git a/lib/Driver/Tools.cpp b/lib/Driver/Tools.cpp index 3a8a28775790a3ac10f08c3f8ee62ffe3d1243a8..af8696c42a7bbf6182cf128806ded91d0ba73e59 100644 --- a/lib/Driver/Tools.cpp +++ b/lib/Driver/Tools.cpp @@ -1995,6 +1995,21 @@ static bool shouldEnableVectorizerAtOLevel(const ArgList &Args) { return false; } +/// Add -x lang to \p CmdArgs for \p Input. +static void addDashXForInput(const ArgList &Args, const InputInfo &Input, + ArgStringList &CmdArgs) { + // When using -verify-pch, we don't want to provide the type + // 'precompiled-header' if it was inferred from the file extension + if (Args.hasArg(options::OPT_verify_pch) && Input.getType() == types::TY_PCH) + return; + + CmdArgs.push_back("-x"); + if (Args.hasArg(options::OPT_rewrite_objc)) + CmdArgs.push_back(types::getTypeName(types::TY_PP_ObjCXX)); + else + CmdArgs.push_back(types::getTypeName(Input.getType())); +} + void Clang::ConstructJob(Compilation &C, const JobAction &JA, const InputInfo &Output, const InputInfoList &Inputs, @@ -2055,7 +2070,10 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, assert(isa<CompileJobAction>(JA) && "Invalid action for clang tool."); if (JA.getType() == types::TY_Nothing) { - CmdArgs.push_back("-fsyntax-only"); + if (Args.hasArg(options::OPT_verify_pch)) + CmdArgs.push_back("-verify-pch"); + else + CmdArgs.push_back("-fsyntax-only"); } else if (JA.getType() == types::TY_LLVM_IR || JA.getType() == types::TY_LTO_IR) { CmdArgs.push_back("-emit-llvm"); @@ -3742,11 +3760,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, for (InputInfoList::const_iterator it = Inputs.begin(), ie = Inputs.end(); it != ie; ++it) { const InputInfo &II = *it; - CmdArgs.push_back("-x"); - if (Args.hasArg(options::OPT_rewrite_objc)) - CmdArgs.push_back(types::getTypeName(types::TY_PP_ObjCXX)); - else - CmdArgs.push_back(types::getTypeName(II.getType())); + + addDashXForInput(Args, II, CmdArgs); + if (II.isFilename()) CmdArgs.push_back(II.getFilename()); else diff --git a/lib/Driver/Types.cpp b/lib/Driver/Types.cpp index d947ae7c03d615c44584708544ba1ec055e8dc85..3538dbc2c0e998f2b4add5c7c3620a8fc4d3a15e 100644 --- a/lib/Driver/Types.cpp +++ b/lib/Driver/Types.cpp @@ -174,6 +174,8 @@ types::ID types::lookupTypeForExtension(const char *Ext) { .Case("F95", TY_Fortran) .Case("mii", TY_PP_ObjCXX) .Case("pcm", TY_ModuleFile) + .Case("pch", TY_PCH) + .Case("gch", TY_PCH) .Default(TY_INVALID); } diff --git a/lib/Frontend/CompilerInstance.cpp b/lib/Frontend/CompilerInstance.cpp index 0d2af334d79acdade20f85aae2e9b3b675a88f13..d8381a824c7d11ace3352299db8bb3eaf6824912 100644 --- a/lib/Frontend/CompilerInstance.cpp +++ b/lib/Frontend/CompilerInstance.cpp @@ -292,12 +292,14 @@ void CompilerInstance::createASTContext() { void CompilerInstance::createPCHExternalASTSource(StringRef Path, bool DisablePCHValidation, bool AllowPCHWithCompilerErrors, + bool AllowConfigurationMismatch, void *DeserializationListener){ OwningPtr<ExternalASTSource> Source; bool Preamble = getPreprocessorOpts().PrecompiledPreambleBytes.first != 0; Source.reset(createPCHExternalASTSource(Path, getHeaderSearchOpts().Sysroot, DisablePCHValidation, AllowPCHWithCompilerErrors, + AllowConfigurationMismatch, getPreprocessor(), getASTContext(), DeserializationListener, Preamble, @@ -311,6 +313,7 @@ CompilerInstance::createPCHExternalASTSource(StringRef Path, const std::string &Sysroot, bool DisablePCHValidation, bool AllowPCHWithCompilerErrors, + bool AllowConfigurationMismatch, Preprocessor &PP, ASTContext &Context, void *DeserializationListener, @@ -321,6 +324,7 @@ CompilerInstance::createPCHExternalASTSource(StringRef Path, Sysroot.empty() ? "" : Sysroot.c_str(), DisablePCHValidation, AllowPCHWithCompilerErrors, + AllowConfigurationMismatch, UseGlobalModuleIndex)); Reader->setDeserializationListener( @@ -329,7 +333,9 @@ CompilerInstance::createPCHExternalASTSource(StringRef Path, Preamble ? serialization::MK_Preamble : serialization::MK_PCH, SourceLocation(), - ASTReader::ARR_None)) { + AllowConfigurationMismatch + ? ASTReader::ARR_ConfigurationMismatch + : ASTReader::ARR_None)) { case ASTReader::Success: // Set the predefines buffer as suggested by the PCH reader. Typically, the // predefines buffer will be empty. @@ -1158,6 +1164,7 @@ CompilerInstance::loadModule(SourceLocation ImportLoc, Sysroot.empty() ? "" : Sysroot.c_str(), PPOpts.DisablePCHValidation, /*AllowASTWithCompilerErrors=*/false, + /*AllowConfigurationMismatch=*/false, getFrontendOpts().UseGlobalModuleIndex); if (hasASTConsumer()) { ModuleManager->setDeserializationListener( diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 92202b7ee201f20a4e187e6d7b1879dc356ea66c..da58a0386e62c21792fa5bf9e19c98f762b27b4e 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -700,6 +700,8 @@ static InputKind ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args, Opts.ProgramAction = frontend::ParseSyntaxOnly; break; case OPT_module_file_info: Opts.ProgramAction = frontend::ModuleFileInfo; break; + case OPT_verify_pch: + Opts.ProgramAction = frontend::VerifyPCH; break; case OPT_print_decl_contexts: Opts.ProgramAction = frontend::PrintDeclContext; break; case OPT_print_preamble: @@ -1585,6 +1587,7 @@ static void ParsePreprocessorOutputArgs(PreprocessorOutputOptions &Opts, case frontend::GeneratePTH: case frontend::ParseSyntaxOnly: case frontend::ModuleFileInfo: + case frontend::VerifyPCH: case frontend::PluginAction: case frontend::PrintDeclContext: case frontend::RewriteObjC: diff --git a/lib/Frontend/FrontendAction.cpp b/lib/Frontend/FrontendAction.cpp index 0baf3e5e1fb325e7d7518b0a9a11a7fa55c92d9c..13a0787e4270c547cc792075c3764afaead411e0 100644 --- a/lib/Frontend/FrontendAction.cpp +++ b/lib/Frontend/FrontendAction.cpp @@ -314,6 +314,7 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI, CI.getPreprocessorOpts().ImplicitPCHInclude, CI.getPreprocessorOpts().DisablePCHValidation, CI.getPreprocessorOpts().AllowPCHWithCompilerErrors, + /*AllowConfigurationMismatch*/false, DeserialListener); if (!CI.getASTContext().getExternalSource()) goto failure; diff --git a/lib/Frontend/FrontendActions.cpp b/lib/Frontend/FrontendActions.cpp index 0d78bf032e3e896cff33c44e5a5fceb5c7609621..8b174605ffd88b1512f335e63260ea4013a85fc7 100644 --- a/lib/Frontend/FrontendActions.cpp +++ b/lib/Frontend/FrontendActions.cpp @@ -320,6 +320,19 @@ ASTConsumer *DumpModuleInfoAction::CreateASTConsumer(CompilerInstance &CI, return new ASTConsumer(); } +ASTConsumer *VerifyPCHAction::CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) { + return new ASTConsumer(); +} + +void VerifyPCHAction::ExecuteAction() { + getCompilerInstance(). + createPCHExternalASTSource(getCurrentFile(), /*DisablePCHValidation*/false, + /*AllowPCHWithCompilerErrors*/false, + /*AllowConfigurationMismatch*/true, + /*DeserializationListener*/0); +} + namespace { /// \brief AST reader listener that dumps module information for a module /// file. diff --git a/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/lib/FrontendTool/ExecuteCompilerInvocation.cpp index e14fb2340e63fed4aa7bee59a77271d87d2f1594..4c2ec441b3f5b50626ace4e9884f726c688f61f1 100644 --- a/lib/FrontendTool/ExecuteCompilerInvocation.cpp +++ b/lib/FrontendTool/ExecuteCompilerInvocation.cpp @@ -65,6 +65,7 @@ static FrontendAction *CreateFrontendBaseAction(CompilerInstance &CI) { case InitOnly: return new InitOnlyAction(); case ParseSyntaxOnly: return new SyntaxOnlyAction(); case ModuleFileInfo: return new DumpModuleInfoAction(); + case VerifyPCH: return new VerifyPCHAction(); case PluginAction: { for (FrontendPluginRegistry::iterator it = diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index 3a93804eab74c2965bf79c89dffc18688c1c4dfe..4d2a400888809e3194fe9d338390b7e50cfae11b 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -1935,7 +1935,7 @@ ASTReader::ReadControlBlock(ModuleFile &F, bool Complain = (ClientLoadCapabilities & ARR_ConfigurationMismatch) == 0; if (Listener && &F == *ModuleMgr.begin() && ParseLanguageOptions(Record, Complain, *Listener) && - !DisableValidation) + !DisableValidation && !AllowConfigurationMismatch) return ConfigurationMismatch; break; } @@ -1944,7 +1944,7 @@ ASTReader::ReadControlBlock(ModuleFile &F, bool Complain = (ClientLoadCapabilities & ARR_ConfigurationMismatch)==0; if (Listener && &F == *ModuleMgr.begin() && ParseTargetOptions(Record, Complain, *Listener) && - !DisableValidation) + !DisableValidation && !AllowConfigurationMismatch) return ConfigurationMismatch; break; } @@ -1953,7 +1953,7 @@ ASTReader::ReadControlBlock(ModuleFile &F, bool Complain = (ClientLoadCapabilities & ARR_ConfigurationMismatch)==0; if (Listener && &F == *ModuleMgr.begin() && ParseDiagnosticOptions(Record, Complain, *Listener) && - !DisableValidation) + !DisableValidation && !AllowConfigurationMismatch) return ConfigurationMismatch; break; } @@ -1962,7 +1962,7 @@ ASTReader::ReadControlBlock(ModuleFile &F, bool Complain = (ClientLoadCapabilities & ARR_ConfigurationMismatch)==0; if (Listener && &F == *ModuleMgr.begin() && ParseFileSystemOptions(Record, Complain, *Listener) && - !DisableValidation) + !DisableValidation && !AllowConfigurationMismatch) return ConfigurationMismatch; break; } @@ -1971,7 +1971,7 @@ ASTReader::ReadControlBlock(ModuleFile &F, bool Complain = (ClientLoadCapabilities & ARR_ConfigurationMismatch)==0; if (Listener && &F == *ModuleMgr.begin() && ParseHeaderSearchOptions(Record, Complain, *Listener) && - !DisableValidation) + !DisableValidation && !AllowConfigurationMismatch) return ConfigurationMismatch; break; } @@ -1981,7 +1981,7 @@ ASTReader::ReadControlBlock(ModuleFile &F, if (Listener && &F == *ModuleMgr.begin() && ParsePreprocessorOptions(Record, Complain, *Listener, SuggestedPredefines) && - !DisableValidation) + !DisableValidation && !AllowConfigurationMismatch) return ConfigurationMismatch; break; } @@ -7618,13 +7618,16 @@ void ASTReader::pushExternalDeclIntoScope(NamedDecl *D, DeclarationName Name) { ASTReader::ASTReader(Preprocessor &PP, ASTContext &Context, StringRef isysroot, bool DisableValidation, - bool AllowASTWithCompilerErrors, bool UseGlobalIndex) + bool AllowASTWithCompilerErrors, + bool AllowConfigurationMismatch, + bool UseGlobalIndex) : Listener(new PCHValidator(PP, *this)), DeserializationListener(0), SourceMgr(PP.getSourceManager()), FileMgr(PP.getFileManager()), Diags(PP.getDiagnostics()), SemaObj(0), PP(PP), Context(Context), Consumer(0), ModuleMgr(PP.getFileManager()), isysroot(isysroot), DisableValidation(DisableValidation), AllowASTWithCompilerErrors(AllowASTWithCompilerErrors), + AllowConfigurationMismatch(AllowConfigurationMismatch), UseGlobalIndex(UseGlobalIndex), TriedLoadingGlobalIndex(false), CurrentGeneration(0), CurrSwitchCaseStmts(&SwitchCaseStmts), NumSLocEntriesRead(0), TotalNumSLocEntries(0), diff --git a/test/Driver/verify_pch.m b/test/Driver/verify_pch.m new file mode 100644 index 0000000000000000000000000000000000000000..c6eca4268bd7945b7824c1aec8da7e59c1bbfd6e --- /dev/null +++ b/test/Driver/verify_pch.m @@ -0,0 +1,12 @@ +// RUN: touch %t.pch +// RUN: %clang -### -verify-pch %t.pch 2> %t.log.1 +// RUN: FileCheck %s < %t.log.1 +// CHECK: -verify-pch + +// Also ensure that the language setting is not affected by the .pch extension +// CHECK-NOT: "-x" "precompiled-header" + +// RUN: %clang -### -verify-pch -x objective-c %t.pch 2> %t.log.2 +// RUN: FileCheck -check-prefix=CHECK2 %s < %t.log.2 +// CHECK2: "-x" "objective-c" +// CHECK2-NOT: "-x" "precompiled-header" diff --git a/test/PCH/verify_pch.m b/test/PCH/verify_pch.m new file mode 100644 index 0000000000000000000000000000000000000000..9870231d7c8c895c8af74ac544b19fec8924618a --- /dev/null +++ b/test/PCH/verify_pch.m @@ -0,0 +1,15 @@ +// Precompile +// RUN: cp %s %t.h +// RUN: %clang_cc1 -x objective-c-header -emit-pch -o %t.pch %t.h + +// Verify successfully +// RUN: %clang_cc1 -x objective-c -verify-pch %t.pch + +// Incompatible lang options ignored +// RUN: %clang_cc1 -x objective-c -fno-builtin -verify-pch %t.pch + +// Stale dependency +// RUN: echo ' ' >> %t.h +// RUN: not %clang_cc1 -x objective-c -verify-pch %t.pch 2> %t.log.2 +// RUN: FileCheck -check-prefix=CHECK-STALE-DEP %s < %t.log.2 +// CHECK-STALE-DEP: file '{{.*}}.h' has been modified since the precompiled header '{{.*}}.pch' was built