diff --git a/clang-tools-extra/clangd/TUScheduler.cpp b/clang-tools-extra/clangd/TUScheduler.cpp --- a/clang-tools-extra/clangd/TUScheduler.cpp +++ b/clang-tools-extra/clangd/TUScheduler.cpp @@ -925,7 +925,7 @@ } LatestBuild = clang::clangd::buildPreamble( - FileName, *Req.CI, Inputs, StoreInMemory, + FileName, Req.CI->clone(), Inputs, StoreInMemory, [this, Version(Inputs.Version)](ASTContext &Ctx, std::shared_ptr PP, const CanonicalIncludes &CanonIncludes) { diff --git a/clang-tools-extra/clangd/tool/Check.cpp b/clang-tools-extra/clangd/tool/Check.cpp --- a/clang-tools-extra/clangd/tool/Check.cpp +++ b/clang-tools-extra/clangd/tool/Check.cpp @@ -159,7 +159,7 @@ bool buildAST() { log("Building preamble..."); Preamble = - buildPreamble(File, *Invocation, Inputs, /*StoreInMemory=*/true, + buildPreamble(File, Invocation->clone(), Inputs, /*StoreInMemory=*/true, [&](ASTContext &Ctx, std::shared_ptr PP, const CanonicalIncludes &Includes) { if (!Opts.BuildDynamicSymbolIndex) diff --git a/clang/include/clang/Frontend/CompilerInvocation.h b/clang/include/clang/Frontend/CompilerInvocation.h --- a/clang/include/clang/Frontend/CompilerInvocation.h +++ b/clang/include/clang/Frontend/CompilerInvocation.h @@ -92,7 +92,8 @@ CompilerInvocationRefBase(); CompilerInvocationRefBase(const CompilerInvocationRefBase &X); CompilerInvocationRefBase(CompilerInvocationRefBase &&X); - CompilerInvocationRefBase &operator=(CompilerInvocationRefBase X); + CompilerInvocationRefBase & + operator=(const CompilerInvocationRefBase &) = delete; CompilerInvocationRefBase &operator=(CompilerInvocationRefBase &&X); ~CompilerInvocationRefBase(); @@ -187,7 +188,20 @@ /// options, the warning flags, and so on. class CompilerInvocation : public CompilerInvocationRefBase, public CompilerInvocationValueBase { + /// Implementation detail of clone(). + CompilerInvocation(const CompilerInvocation &) = default; + public: + CompilerInvocation() = default; + CompilerInvocation(CompilerInvocation &&) = default; + CompilerInvocation &operator=(CompilerInvocation &&) = default; + CompilerInvocation &operator=(const CompilerInvocation &) = delete; + + /// Create a deep copy. + /// Also copies CompilerInvocationRefBase and its members behind + /// reference-counted pointers. + CompilerInvocation clone() const { return CompilerInvocation(*this); } + /// Create a compiler invocation from a list of input options. /// \returns true on success. /// diff --git a/clang/lib/ARCMigrate/ARCMT.cpp b/clang/lib/ARCMigrate/ARCMT.cpp --- a/clang/lib/ARCMigrate/ARCMT.cpp +++ b/clang/lib/ARCMigrate/ARCMT.cpp @@ -173,8 +173,7 @@ static CompilerInvocation * createInvocationForMigration(CompilerInvocation &origCI, const PCHContainerReader &PCHContainerRdr) { - std::unique_ptr CInvok; - CInvok.reset(new CompilerInvocation(origCI)); + auto CInvok = std::make_unique(origCI.clone()); PreprocessorOptions &PPOpts = CInvok->getPreprocessorOpts(); if (!PPOpts.ImplicitPCHInclude.empty()) { // We can't use a PCH because it was likely built in non-ARC mode and we @@ -346,13 +345,13 @@ LangOptions::GCMode OrigGCMode = origCI.getLangOpts()->getGC(); // Make sure checking is successful first. - CompilerInvocation CInvokForCheck(origCI); + CompilerInvocation CInvokForCheck(origCI.clone()); if (arcmt::checkForManualIssues(CInvokForCheck, Input, PCHContainerOps, DiagClient, emitPremigrationARCErrors, plistOut)) return true; - CompilerInvocation CInvok(origCI); + CompilerInvocation CInvok(origCI.clone()); CInvok.getFrontendOpts().Inputs.clear(); CInvok.getFrontendOpts().Inputs.push_back(Input); @@ -510,7 +509,7 @@ const CompilerInvocation &CI, std::shared_ptr PCHContainerOps, DiagnosticConsumer *diagClient, StringRef outputDir) - : OrigCI(CI), PCHContainerOps(std::move(PCHContainerOps)), + : OrigCI(CI.clone()), PCHContainerOps(std::move(PCHContainerOps)), DiagClient(diagClient), HadARCErrors(false) { if (!outputDir.empty()) { IntrusiveRefCntPtr DiagID(new DiagnosticIDs()); diff --git a/clang/lib/Frontend/ASTUnit.cpp b/clang/lib/Frontend/ASTUnit.cpp --- a/clang/lib/Frontend/ASTUnit.cpp +++ b/clang/lib/Frontend/ASTUnit.cpp @@ -1104,7 +1104,7 @@ assert(VFS == &FileMgr->getVirtualFileSystem() && "VFS passed to Parse and VFS in FileMgr are different"); - auto CCInvocation = std::make_shared(*Invocation); + auto CCInvocation = std::make_shared(Invocation->clone()); if (OverrideMainBuffer) { assert(Preamble && "No preamble was built, but OverrideMainBuffer is not null"); @@ -2131,7 +2131,7 @@ CompletionTimer.setOutput("Code completion @ " + File + ":" + Twine(Line) + ":" + Twine(Column)); - auto CCInvocation = std::make_shared(*Invocation); + auto CCInvocation = std::make_shared(Invocation->clone()); FrontendOptions &FrontendOpts = CCInvocation->getFrontendOpts(); CodeCompleteOptions &CodeCompleteOpts = FrontendOpts.CodeCompleteOpts; diff --git a/clang/lib/Frontend/ChainedIncludesSource.cpp b/clang/lib/Frontend/ChainedIncludesSource.cpp --- a/clang/lib/Frontend/ChainedIncludesSource.cpp +++ b/clang/lib/Frontend/ChainedIncludesSource.cpp @@ -124,8 +124,8 @@ for (unsigned i = 0, e = includes.size(); i != e; ++i) { bool firstInclude = (i == 0); - std::unique_ptr CInvok; - CInvok.reset(new CompilerInvocation(CI.getInvocation())); + auto CInvok = + std::make_unique(CI.getInvocation().clone()); CInvok->getPreprocessorOpts().ChainedIncludes.clear(); CInvok->getPreprocessorOpts().ImplicitPCHInclude.clear(); diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp --- a/clang/lib/Frontend/CompilerInstance.cpp +++ b/clang/lib/Frontend/CompilerInstance.cpp @@ -1041,8 +1041,8 @@ llvm::TimeTraceScope TimeScope("Module Compile", ModuleName); // Construct a compiler invocation for creating this module. - auto Invocation = - std::make_shared(ImportingInstance.getInvocation()); + auto Invocation = std::make_shared( + ImportingInstance.getInvocation().clone()); PreprocessorOptions &PPOpts = Invocation->getPreprocessorOpts(); diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -128,17 +128,6 @@ CompilerInvocationRefBase::CompilerInvocationRefBase( CompilerInvocationRefBase &&X) = default; -CompilerInvocationRefBase & -CompilerInvocationRefBase::operator=(CompilerInvocationRefBase X) { - LangOpts.swap(X.LangOpts); - TargetOpts.swap(X.TargetOpts); - DiagnosticOpts.swap(X.DiagnosticOpts); - HeaderSearchOpts.swap(X.HeaderSearchOpts); - PreprocessorOpts.swap(X.PreprocessorOpts); - AnalyzerOpts.swap(X.AnalyzerOpts); - return *this; -} - CompilerInvocationRefBase & CompilerInvocationRefBase::operator=(CompilerInvocationRefBase &&X) = default; diff --git a/clang/lib/Frontend/PrecompiledPreamble.cpp b/clang/lib/Frontend/PrecompiledPreamble.cpp --- a/clang/lib/Frontend/PrecompiledPreamble.cpp +++ b/clang/lib/Frontend/PrecompiledPreamble.cpp @@ -317,7 +317,8 @@ PreambleCallbacks &Callbacks) { assert(VFS && "VFS is null"); - auto PreambleInvocation = std::make_shared(Invocation); + auto PreambleInvocation = + std::make_shared(Invocation.clone()); FrontendOptions &FrontendOpts = PreambleInvocation->getFrontendOpts(); PreprocessorOptions &PreprocessorOpts = PreambleInvocation->getPreprocessorOpts(); @@ -501,7 +502,8 @@ Bounds.Size <= MainFileBuffer.getBufferSize() && "Buffer is too large. Bounds were calculated from a different buffer?"); - auto PreambleInvocation = std::make_shared(Invocation); + auto PreambleInvocation = + std::make_shared(Invocation.clone()); PreprocessorOptions &PreprocessorOpts = PreambleInvocation->getPreprocessorOpts(); diff --git a/clang/lib/Frontend/Rewrite/FrontendActions.cpp b/clang/lib/Frontend/Rewrite/FrontendActions.cpp --- a/clang/lib/Frontend/Rewrite/FrontendActions.cpp +++ b/clang/lib/Frontend/Rewrite/FrontendActions.cpp @@ -244,7 +244,7 @@ CompilerInstance Instance(CI.getPCHContainerOperations(), &CI.getModuleCache()); Instance.setInvocation( - std::make_shared(CI.getInvocation())); + std::make_shared(CI.getInvocation().clone())); Instance.createDiagnostics( new ForwardingDiagnosticConsumer(CI.getDiagnosticClient()), /*ShouldOwnClient=*/true); diff --git a/clang/lib/StaticAnalyzer/Frontend/ModelInjector.cpp b/clang/lib/StaticAnalyzer/Frontend/ModelInjector.cpp --- a/clang/lib/StaticAnalyzer/Frontend/ModelInjector.cpp +++ b/clang/lib/StaticAnalyzer/Frontend/ModelInjector.cpp @@ -64,7 +64,8 @@ return; } - auto Invocation = std::make_shared(CI.getInvocation()); + auto Invocation = + std::make_shared(CI.getInvocation().clone()); FrontendOptions &FrontendOpts = Invocation->getFrontendOpts(); InputKind IK = Language::CXX; // FIXME diff --git a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp --- a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp +++ b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp @@ -21,7 +21,7 @@ static CompilerInvocation makeInvocationForModuleBuildWithoutPaths(const ModuleDeps &Deps) { // Make a deep copy of the invocation. - CompilerInvocation CI(*Deps.Invocation); + CompilerInvocation CI(Deps.Invocation->clone()); // Remove options incompatible with explicit module build. CI.getFrontendOpts().Inputs.clear(); diff --git a/clang/unittests/Frontend/CompilerInvocationTest.cpp b/clang/unittests/Frontend/CompilerInvocationTest.cpp --- a/clang/unittests/Frontend/CompilerInvocationTest.cpp +++ b/clang/unittests/Frontend/CompilerInvocationTest.cpp @@ -97,29 +97,34 @@ ASSERT_THAT(Array, ContainsN(StrEq("x"), 2)); } -// Copy constructor/assignment perform deep copy of reference-counted pointers. +// Clone performs deep copy of reference-counted pointers. -TEST(CompilerInvocationTest, DeepCopyConstructor) { +TEST(CompilerInvocationTest, CloneWithMoveConstructor) { CompilerInvocation A; A.getAnalyzerOpts()->Config["Key"] = "Old"; - CompilerInvocation B(A); + CompilerInvocation B(A.clone()); B.getAnalyzerOpts()->Config["Key"] = "New"; ASSERT_EQ(A.getAnalyzerOpts()->Config["Key"], "Old"); } -TEST(CompilerInvocationTest, DeepCopyAssignment) { +TEST(CompilerInvocationTest, CloneWithMoveAssignment) { CompilerInvocation A; A.getAnalyzerOpts()->Config["Key"] = "Old"; CompilerInvocation B; - B = A; + B = A.clone(); B.getAnalyzerOpts()->Config["Key"] = "New"; ASSERT_EQ(A.getAnalyzerOpts()->Config["Key"], "Old"); } +TEST(CompilerInvocationTest, NotCopyConstructibleNorAssignable) { + ASSERT_FALSE(std::is_copy_constructible::value); + ASSERT_FALSE(std::is_copy_assignable::value); +} + // Boolean option with a keypath that defaults to true. // The only flag with a negative spelling can set the keypath to false.