https://github.com/jansvoboda11 created https://github.com/llvm/llvm-project/pull/205686
After https://github.com/swiftlang/llvm-project/pull/13201, the only clients of the compiler invocation's `visitPaths()` APIs call it on the cow variant. This PR moves the implementation from the base class into the cow, allowing specialized copy-on-write behavior for mutating visitation. If the callback requests a path to be mutated, exclusive ownership of the containing `*Options` instance is established and the string gets modified. This should be performance win for dependency scans using prefix mapping, although admittedly I haven't benchmarked. >From bc6447f27fdf093191b8fa1f9db57d62abbb01b7 Mon Sep 17 00:00:00 2001 From: Jan Svoboda <[email protected]> Date: Wed, 24 Jun 2026 11:03:05 -0700 Subject: [PATCH 1/2] [clang][deps] Avoid `CompilerInvocation` copies When constructing the dependency graph for compilation caching, the dependency scanner needs to do some extra operations on the compiler invocations. Historically, these have not utilized the copy-on-write variant well. This patch takes care to minimize `CompilerInvocation` copies, which improves incremental scans with populated up-to-date scanning module cache by 16-18%. Together with https://github.com/llvm/llvm-project/pull/203350 which operates in the same space, wall-times are improved by 1.54x and instruction counts by 1.66x. --- .../DependencyActionController.h | 2 +- .../clang/Frontend/CompilerInvocation.h | 80 ++++++++++++++++++- .../DependencyScannerImpl.cpp | 12 ++- clang/lib/Frontend/CompilerInvocation.cpp | 23 ++++++ clang/lib/Tooling/DependencyScanningTool.cpp | 6 +- 5 files changed, 118 insertions(+), 5 deletions(-) diff --git a/clang/include/clang/DependencyScanning/DependencyActionController.h b/clang/include/clang/DependencyScanning/DependencyActionController.h index 024b0de9048ec..023f080b767cc 100644 --- a/clang/include/clang/DependencyScanning/DependencyActionController.h +++ b/clang/include/clang/DependencyScanning/DependencyActionController.h @@ -61,7 +61,7 @@ class DependencyActionController { /// Finalizes the scan instance and modifies the resulting TU invocation. /// Returns true on success, false on failure. virtual bool finalize(CompilerInstance &ScanInstance, - CompilerInvocation &NewInvocation) { + CowCompilerInvocation &NewInvocation) { return true; } diff --git a/clang/include/clang/Frontend/CompilerInvocation.h b/clang/include/clang/Frontend/CompilerInvocation.h index 03097aefacf50..a3bd41a70a4ec 100644 --- a/clang/include/clang/Frontend/CompilerInvocation.h +++ b/clang/include/clang/Frontend/CompilerInvocation.h @@ -21,8 +21,10 @@ #include "clang/Frontend/MigratorOptions.h" #include "clang/Frontend/PreprocessorOutputOptions.h" #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" -#include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/IntrusiveRefCntPtr.h" +#include "llvm/ADT/ScopeExit.h" + #include <memory> #include <string> @@ -127,6 +129,9 @@ class CompilerInvocationBase { /// prevent creation of the reference-counted option objects. struct EmptyConstructor {}; + /// Tag for the shallow-copy constructor below. + struct ShallowConstructor {}; + CompilerInvocationBase(); CompilerInvocationBase(EmptyConstructor) {} CompilerInvocationBase(const CompilerInvocationBase &X) = delete; @@ -251,6 +256,15 @@ class CompilerInvocation : public CompilerInvocationBase { explicit CompilerInvocation(const CowCompilerInvocation &X); CompilerInvocation &operator=(const CowCompilerInvocation &X); + /// Move-construct/move-assign from a \c CowCompilerInvocation. Steals the + /// (potentially copy-on-written) option group pointers without deep-copying; + /// \p X is left empty. Useful to receive results of mutating a temporary + /// Cow alias back into a \c CompilerInvocation. + /// @{ + explicit CompilerInvocation(CowCompilerInvocation &&X); + CompilerInvocation &operator=(CowCompilerInvocation &&X); + /// @} + /// Const getters. /// @{ // Note: These need to be pulled in manually. Otherwise, they get hidden by @@ -293,6 +307,22 @@ class CompilerInvocation : public CompilerInvocationBase { ssaf::SSAFOptions &getSSAFOpts() { return *SSAFOpts; } /// @} + /// Invokes the \a Fn with CowCompilerInvocation representing \c this. + /// The \a Fn must not directly modify \c this. + /// The provided \c CowCompilerInvocation must not escape \a Fn. + template <class R> + R withCowRef(llvm::function_ref<R(CowCompilerInvocation &)> Fn); + template <class R> + R withCowRef(llvm::function_ref<R(const CowCompilerInvocation &)> Fn) const; + + /// Visitation. + /// @{ + /// Visits paths stored in the invocation. The callback may return true to + /// short-circuit the visitation, or return false to continue visiting. This + /// is allowed to mutate the visited paths. + void visitPaths(llvm::function_ref<bool(std::string &)> Callback); + /// @} + /// Create a compiler invocation from a list of input options. /// \returns true on success. /// @@ -385,6 +415,16 @@ class CowCompilerInvocation : public CompilerInvocationBase { CowCompilerInvocation(CompilerInvocation &&X) : CompilerInvocationBase(std::move(X)) {} + /// Construct a CowCompilerInvocation that aliases the option storage of \p + /// X without deep-copying. Subsequent mutations through getMut*Opts() will + /// copy-on-write per group as usual, leaving \p X unaffected. The caller + /// must guarantee that \p X is not mutated for the lifetime of the + /// constructed invocation. + CowCompilerInvocation(ShallowConstructor, const CompilerInvocation &X) + : CompilerInvocationBase(EmptyConstructor{}) { + shallow_copy_assign(X); + } + // Const getters are inherited from the base class. /// Mutable getters. @@ -404,8 +444,46 @@ class CowCompilerInvocation : public CompilerInvocationBase { PreprocessorOutputOptions &getMutPreprocessorOutputOpts(); ssaf::SSAFOptions &getMutSSAFOpts(); /// @} + + /// Visits paths stored in the invocation, allowing the callback to mutate + /// them. To preserve the copy-on-write invariant for groups whose paths the + /// caller might modify, this ensures unique ownership of every option group + /// up front; if the callback only inspects (and does not mutate) the paths, + /// the const \c visitPaths overload should be used instead to avoid those + /// per-group copies. + void visitMutPaths(llvm::function_ref<bool(std::string &)> Callback); }; +template <class R> +R CompilerInvocation::withCowRef( + llvm::function_ref<R(CowCompilerInvocation &)> Fn) { + // We use moves to avoid bumping the ref-count of the shared_ptr that holds + // individual options. Since we expect \a Fn to actually modify \c CowRef, + // this prevents temporary copies. + CowCompilerInvocation CowRef = std::move(*this); + llvm::scope_exit Mutate([&]() { *this = std::move(CowRef); }); + return Fn(CowRef); +} + +template <class R> +R CompilerInvocation::withCowRef( + llvm::function_ref<R(const CowCompilerInvocation &)> Fn) const { + // We use the shallow constructor. Since \a Fn cannot modify \c CowRef, no + // copies will be created, despite the bump to the ref-count of the shared_ptr + // that holds individual options. + CowCompilerInvocation CowRef(ShallowConstructor{}, *this); + return Fn(CowRef); +} + +inline CompilerInvocation::CompilerInvocation(CowCompilerInvocation &&X) + : CompilerInvocationBase(std::move(X)) {} + +inline CompilerInvocation & +CompilerInvocation::operator=(CowCompilerInvocation &&X) { + CompilerInvocationBase::operator=(std::move(X)); + return *this; +} + IntrusiveRefCntPtr<llvm::vfs::FileSystem> createVFSFromCompilerInvocation(const CompilerInvocation &CI, DiagnosticsEngine &Diags); diff --git a/clang/lib/DependencyScanning/DependencyScannerImpl.cpp b/clang/lib/DependencyScanning/DependencyScannerImpl.cpp index dc3dbe3603c01..68fda9227dfcb 100644 --- a/clang/lib/DependencyScanning/DependencyScannerImpl.cpp +++ b/clang/lib/DependencyScanning/DependencyScannerImpl.cpp @@ -713,7 +713,11 @@ bool DependencyScanningAction::runInvocation( if (MDC) MDC->applyDiscoveredDependencies(*OriginalInvocation); - if (!Controller.finalize(ScanInstance, *OriginalInvocation)) + bool Success = OriginalInvocation->withCowRef<bool>( + [&](CowCompilerInvocation &CowOriginalInvocation) { + return Controller.finalize(ScanInstance, CowOriginalInvocation); + }); + if (!Success) return false; Consumer.handleBuildCommand( @@ -791,7 +795,11 @@ bool DependencyScanningAction::runInvocation( MDC->applyDiscoveredDependencies(*OriginalInvocation); } - if (!Controller.finalize(ScanInstance, *OriginalInvocation)) + bool Success = OriginalInvocation->withCowRef<bool>( + [&](CowCompilerInvocation &CowOriginalInvocation) { + return Controller.finalize(ScanInstance, CowOriginalInvocation); + }); + if (!Success) return false; Consumer.handleBuildCommand( diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index dfde7b756dbff..e2260eb0d078a 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -5435,6 +5435,29 @@ void CompilerInvocationBase::visitPaths( [&Callback](std::string &Path) { return Callback(StringRef(Path)); }); } +void CowCompilerInvocation::visitMutPaths( + llvm::function_ref<bool(std::string &)> Callback) { + // Ensure exclusive ownership of every option group, so that visitPathsImpl() + // doesn't affect any other invocations. + // FIXME: Do this only if \c Callback does decide to modify any strings in an + // option group. + (void)ensureOwned(LangOpts); + (void)ensureOwned(TargetOpts); + (void)ensureOwned(DiagnosticOpts); + (void)ensureOwned(HSOpts); + (void)ensureOwned(PPOpts); + (void)ensureOwned(AnalyzerOpts); + (void)ensureOwned(MigratorOpts); + (void)ensureOwned(APINotesOpts); + (void)ensureOwned(CodeGenOpts); + (void)ensureOwned(FSOpts); + (void)ensureOwned(FrontendOpts); + (void)ensureOwned(DependencyOutputOpts); + (void)ensureOwned(PreprocessorOutputOpts); + (void)ensureOwned(SSAFOpts); + visitPathsImpl(Callback); +} + void CompilerInvocationBase::generateCC1CommandLine( ArgumentConsumer Consumer) const { llvm::Triple T(getTargetOpts().Triple); diff --git a/clang/lib/Tooling/DependencyScanningTool.cpp b/clang/lib/Tooling/DependencyScanningTool.cpp index d55367107862d..11b225830c2fc 100644 --- a/clang/lib/Tooling/DependencyScanningTool.cpp +++ b/clang/lib/Tooling/DependencyScanningTool.cpp @@ -587,7 +587,11 @@ bool CompilerInstanceWithContext::computeDependencies( MDC->run(Consumer); MDC->applyDiscoveredDependencies(ModuleInvocation); - if (!Controller.finalize(CI, ModuleInvocation)) + bool Success = ModuleInvocation.withCowRef<bool>( + [&](CowCompilerInvocation &CowModuleInvocation) { + return Controller.finalize(CI, CowModuleInvocation); + }); + if (!Success) return false; Consumer.handleBuildCommand( >From f5cc14dfb74aef8fb7580a08bb85bfb2d87f8c70 Mon Sep 17 00:00:00 2001 From: Jan Svoboda <[email protected]> Date: Tue, 23 Jun 2026 15:53:19 -0700 Subject: [PATCH 2/2] [clang] Specialize invocation path visitation to the cow --- .../clang/Frontend/CompilerInvocation.h | 50 ++++--- .../include/clang/Frontend/FrontendOptions.h | 2 +- .../DependencyScanning/ModuleDepCollector.cpp | 4 +- clang/lib/Frontend/CompilerInvocation.cpp | 123 ++++++++---------- 4 files changed, 77 insertions(+), 102 deletions(-) diff --git a/clang/include/clang/Frontend/CompilerInvocation.h b/clang/include/clang/Frontend/CompilerInvocation.h index a3bd41a70a4ec..f71969d515be0 100644 --- a/clang/include/clang/Frontend/CompilerInvocation.h +++ b/clang/include/clang/Frontend/CompilerInvocation.h @@ -165,13 +165,6 @@ class CompilerInvocationBase { const ssaf::SSAFOptions &getSSAFOpts() const { return *SSAFOpts; } /// @} - /// Visitation. - /// @{ - /// Visits paths stored in the invocation. The callback may return true to - /// short-circuit the visitation, or return false to continue visiting. - void visitPaths(llvm::function_ref<bool(StringRef)> Callback) const; - /// @} - /// Command line generation. /// @{ using StringAllocator = llvm::function_ref<const char *(const Twine &)>; @@ -206,12 +199,6 @@ class CompilerInvocationBase { /// This is a (less-efficient) wrapper over generateCC1CommandLine(). std::vector<std::string> getCC1CommandLine() const; -protected: - /// Visits paths stored in the invocation. This is generally unsafe to call - /// directly, and each sub-class need to ensure calling this doesn't violate - /// its invariants. - void visitPathsImpl(llvm::function_ref<bool(std::string &)> Predicate); - private: /// Generate command line options from DiagnosticOptions. static void GenerateDiagnosticArgs(const DiagnosticOptions &Opts, @@ -315,14 +302,6 @@ class CompilerInvocation : public CompilerInvocationBase { template <class R> R withCowRef(llvm::function_ref<R(const CowCompilerInvocation &)> Fn) const; - /// Visitation. - /// @{ - /// Visits paths stored in the invocation. The callback may return true to - /// short-circuit the visitation, or return false to continue visiting. This - /// is allowed to mutate the visited paths. - void visitPaths(llvm::function_ref<bool(std::string &)> Callback); - /// @} - /// Create a compiler invocation from a list of input options. /// \returns true on success. /// @@ -445,13 +424,30 @@ class CowCompilerInvocation : public CompilerInvocationBase { ssaf::SSAFOptions &getMutSSAFOpts(); /// @} + /// The result of mutable visitation. + struct VisitMutResult { + /// Whether to replace the given StringRef with the modified std::string &. + bool Replace = false; + /// Whether to short-circuit the visitation. + bool Terminate = false; + }; + /// Visits paths stored in the invocation, allowing the callback to mutate - /// them. To preserve the copy-on-write invariant for groups whose paths the - /// caller might modify, this ensures unique ownership of every option group - /// up front; if the callback only inspects (and does not mutate) the paths, - /// the const \c visitPaths overload should be used instead to avoid those - /// per-group copies. - void visitMutPaths(llvm::function_ref<bool(std::string &)> Callback); + /// them via the out-param. This upholds the same copy-on-write semantics as + /// the mutable getters. + void visitMutPaths( + llvm::function_ref<VisitMutResult(StringRef, std::string &)> Cb); + + /// The result of const visitation. + struct VisitConstResult { + /// Whether to short-circuit the visitation. + bool Terminate = false; + + operator VisitMutResult() const { return {/*Replace=*/false, Terminate}; } + }; + + /// Visits paths stored in the invocation. + void visitPaths(llvm::function_ref<VisitConstResult(StringRef)> Cb) const; }; template <class R> diff --git a/clang/include/clang/Frontend/FrontendOptions.h b/clang/include/clang/Frontend/FrontendOptions.h index a8627ea5d47a4..f65547e68b29d 100644 --- a/clang/include/clang/Frontend/FrontendOptions.h +++ b/clang/include/clang/Frontend/FrontendOptions.h @@ -241,7 +241,7 @@ class FrontendInputFile { /// Whether we're dealing with a 'system' input (vs. a 'user' input). bool IsSystem = false; - friend class CompilerInvocationBase; + friend class CowCompilerInvocation; public: FrontendInputFile() = default; diff --git a/clang/lib/DependencyScanning/ModuleDepCollector.cpp b/clang/lib/DependencyScanning/ModuleDepCollector.cpp index c4ca0a35f4c30..c7b7fcc8750c3 100644 --- a/clang/lib/DependencyScanning/ModuleDepCollector.cpp +++ b/clang/lib/DependencyScanning/ModuleDepCollector.cpp @@ -471,9 +471,9 @@ static bool isSafeToIgnoreCWD(const CowCompilerInvocation &CI) { // command line inputs use relative paths. bool AnyRelative = false; CI.visitPaths([&](StringRef Path) { - assert(!AnyRelative && "Continuing path visitation despite returning true"); + assert(!AnyRelative && "Continuing path visitation despite relative path"); AnyRelative |= !Path.empty() && !llvm::sys::path::is_absolute(Path); - return AnyRelative; + return CowCompilerInvocation::VisitConstResult{/*Terminate=*/AnyRelative}; }); return !AnyRelative; } diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index e2260eb0d078a..7da6a06ad2c73 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -5354,108 +5354,87 @@ std::string CompilerInvocation::computeContextHash() const { return toString(llvm::APInt(64, Hash), 36, /*Signed=*/false); } -void CompilerInvocationBase::visitPathsImpl( - llvm::function_ref<bool(std::string &)> Predicate) { -#define RETURN_IF(PATH) \ +void CowCompilerInvocation::visitMutPaths( + llvm::function_ref<VisitMutResult(StringRef, std::string &)> Cb) { + std::string NewValue; + +#define RETURN_IF(OPTS, PATH) \ do { \ - if (Predicate(PATH)) \ + VisitMutResult Res = Cb(PATH, NewValue); \ + if (Res.Replace) { \ + (void)ensureOwned(OPTS); \ + PATH = ""; \ + std::swap(PATH, NewValue); \ + } \ + if (Res.Terminate) \ return; \ } while (0) -#define RETURN_IF_MANY(PATHS) \ +#define RETURN_IF_MANY(OPTS, PATHS) \ do { \ - if (llvm::any_of(PATHS, Predicate)) \ - return; \ + for (unsigned I = 0, E = PATHS.size(); I != E; ++I) \ + RETURN_IF(OPTS, PATHS[I]); \ } while (0) - auto &HeaderSearchOpts = *this->HSOpts; // Header search paths. - RETURN_IF(HeaderSearchOpts.Sysroot); - for (auto &Entry : HeaderSearchOpts.UserEntries) + RETURN_IF(HSOpts, HSOpts->Sysroot); + for (auto &Entry : HSOpts->UserEntries) if (Entry.IgnoreSysRoot) - RETURN_IF(Entry.Path); - RETURN_IF(HeaderSearchOpts.ResourceDir); - RETURN_IF(HeaderSearchOpts.ModuleCachePath); - RETURN_IF(HeaderSearchOpts.ModuleUserBuildPath); - for (auto &[Name, File] : HeaderSearchOpts.PrebuiltModuleFiles) - RETURN_IF(File); - RETURN_IF_MANY(HeaderSearchOpts.PrebuiltModulePaths); - RETURN_IF_MANY(HeaderSearchOpts.VFSOverlayFiles); + RETURN_IF(HSOpts, Entry.Path); + RETURN_IF(HSOpts, HSOpts->ResourceDir); + RETURN_IF(HSOpts, HSOpts->ModuleCachePath); + RETURN_IF(HSOpts, HSOpts->ModuleUserBuildPath); + for (auto &[Name, File] : HSOpts->PrebuiltModuleFiles) + RETURN_IF(HSOpts, File); + RETURN_IF_MANY(HSOpts, HSOpts->PrebuiltModulePaths); + RETURN_IF_MANY(HSOpts, HSOpts->VFSOverlayFiles); // Preprocessor options. - auto &PPOpts = *this->PPOpts; - RETURN_IF_MANY(PPOpts.MacroIncludes); - RETURN_IF_MANY(PPOpts.Includes); - RETURN_IF(PPOpts.ImplicitPCHInclude); + RETURN_IF_MANY(PPOpts, PPOpts->MacroIncludes); + RETURN_IF_MANY(PPOpts, PPOpts->Includes); + RETURN_IF(PPOpts, PPOpts->ImplicitPCHInclude); // Frontend options. - auto &FrontendOpts = *this->FrontendOpts; - for (auto &Input : FrontendOpts.Inputs) { + for (auto &Input : FrontendOpts->Inputs) { if (Input.isBuffer()) continue; - RETURN_IF(Input.File); + RETURN_IF(FrontendOpts, Input.File); } - // TODO: Also report output files such as FrontendOpts.OutputFile; - RETURN_IF(FrontendOpts.CodeCompletionAt.FileName); - RETURN_IF_MANY(FrontendOpts.ModuleMapFiles); - RETURN_IF_MANY(FrontendOpts.ModuleFiles); - RETURN_IF_MANY(FrontendOpts.ModulesEmbedFiles); - RETURN_IF_MANY(FrontendOpts.ASTMergeFiles); - RETURN_IF(FrontendOpts.OverrideRecordLayoutsFile); - RETURN_IF(FrontendOpts.StatsFile); + // TODO: Also report output files such as FrontendOpts->OutputFile; + RETURN_IF(FrontendOpts, FrontendOpts->CodeCompletionAt.FileName); + RETURN_IF_MANY(FrontendOpts, FrontendOpts->ModuleMapFiles); + RETURN_IF_MANY(FrontendOpts, FrontendOpts->ModuleFiles); + RETURN_IF_MANY(FrontendOpts, FrontendOpts->ModulesEmbedFiles); + RETURN_IF_MANY(FrontendOpts, FrontendOpts->ASTMergeFiles); + RETURN_IF(FrontendOpts, FrontendOpts->OverrideRecordLayoutsFile); + RETURN_IF(FrontendOpts, FrontendOpts->StatsFile); // Filesystem options. - auto &FileSystemOpts = *this->FSOpts; - RETURN_IF(FileSystemOpts.WorkingDir); + RETURN_IF(FSOpts, FSOpts->WorkingDir); // Codegen options. - auto &CodeGenOpts = *this->CodeGenOpts; - RETURN_IF(CodeGenOpts.DebugCompilationDir); - RETURN_IF(CodeGenOpts.CoverageCompilationDir); + RETURN_IF(CodeGenOpts, CodeGenOpts->DebugCompilationDir); + RETURN_IF(CodeGenOpts, CodeGenOpts->CoverageCompilationDir); // Sanitizer options. - RETURN_IF_MANY(LangOpts->NoSanitizeFiles); + RETURN_IF_MANY(LangOpts, LangOpts->NoSanitizeFiles); // Coverage mappings. - RETURN_IF(CodeGenOpts.ProfileInstrumentUsePath); - RETURN_IF(CodeGenOpts.SampleProfileFile); - RETURN_IF(CodeGenOpts.ProfileRemappingFile); + RETURN_IF(CodeGenOpts, CodeGenOpts->ProfileInstrumentUsePath); + RETURN_IF(CodeGenOpts, CodeGenOpts->SampleProfileFile); + RETURN_IF(CodeGenOpts, CodeGenOpts->ProfileRemappingFile); // Dependency output options. for (auto &ExtraDep : DependencyOutputOpts->ExtraDeps) - RETURN_IF(ExtraDep.first); + RETURN_IF(DependencyOutputOpts, ExtraDep.first); } -void CompilerInvocationBase::visitPaths( - llvm::function_ref<bool(StringRef)> Callback) const { - // The const_cast here is OK, because visitPathsImpl() itself doesn't modify - // the invocation, and our callback takes immutable StringRefs. - return const_cast<CompilerInvocationBase *>(this)->visitPathsImpl( - [&Callback](std::string &Path) { return Callback(StringRef(Path)); }); -} - -void CowCompilerInvocation::visitMutPaths( - llvm::function_ref<bool(std::string &)> Callback) { - // Ensure exclusive ownership of every option group, so that visitPathsImpl() - // doesn't affect any other invocations. - // FIXME: Do this only if \c Callback does decide to modify any strings in an - // option group. - (void)ensureOwned(LangOpts); - (void)ensureOwned(TargetOpts); - (void)ensureOwned(DiagnosticOpts); - (void)ensureOwned(HSOpts); - (void)ensureOwned(PPOpts); - (void)ensureOwned(AnalyzerOpts); - (void)ensureOwned(MigratorOpts); - (void)ensureOwned(APINotesOpts); - (void)ensureOwned(CodeGenOpts); - (void)ensureOwned(FSOpts); - (void)ensureOwned(FrontendOpts); - (void)ensureOwned(DependencyOutputOpts); - (void)ensureOwned(PreprocessorOutputOpts); - (void)ensureOwned(SSAFOpts); - visitPathsImpl(Callback); +void CowCompilerInvocation::visitPaths( + llvm::function_ref<VisitConstResult(StringRef)> Cb) const { + // The const_cast here is OK, because our callback never tries to modify. + return const_cast<CowCompilerInvocation *>(this)->visitMutPaths( + [&Cb](StringRef Path, std::string &) { return Cb(Path); }); } void CompilerInvocationBase::generateCC1CommandLine( _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
