From b9ecd329ae59e6d35b09b1a25882051636ab00b4 Mon Sep 17 00:00:00 2001 From: Sergei Zimmerman Date: Fri, 17 Oct 2025 02:58:10 +0300 Subject: [PATCH] treewide: Replace a lot of std::function with recursive lambdas via 'deducing this' This is much simpler and has less overhead than a std::function, which does type erasure. --- src/libexpr/eval.cc | 4 +- src/libexpr/primops/fromTOML.cc | 7 ++- src/libflake/flake.cc | 52 +++++++++-------------- src/libflake/lockfile.cc | 12 ++---- src/libstore/derived-path-map.cc | 14 +++--- src/libstore/nar-accessor.cc | 4 +- src/libutil-tests/git.cc | 3 +- src/libutil/include/nix/util/topo-sort.hh | 4 +- src/nix/eval.cc | 5 +-- src/nix/flake-prefetch-inputs.cc | 3 +- src/nix/flake.cc | 4 +- src/nix/search.cc | 8 ++-- src/nix/why-depends.cc | 11 +++-- 13 files changed, 50 insertions(+), 81 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 800c29fad..71d374f77 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -2160,9 +2160,7 @@ void EvalState::forceValueDeep(Value & v) { std::set seen; - std::function recurse; - - recurse = [&](Value & v) { + auto recurse = [&](this auto & recurse, Value & v) { if (!seen.insert(&v).second) return; diff --git a/src/libexpr/primops/fromTOML.cc b/src/libexpr/primops/fromTOML.cc index d2f91a75b..b65f51573 100644 --- a/src/libexpr/primops/fromTOML.cc +++ b/src/libexpr/primops/fromTOML.cc @@ -92,7 +92,7 @@ static void prim_fromTOML(EvalState & state, const PosIdx pos, Value ** args, Va std::istringstream tomlStream(std::string{toml}); - auto visit = [&](auto & self, Value & v, toml::value t) -> void { + auto visit = [&](this auto & visit, Value & v, toml::value t) -> void { switch (t.type()) { case toml::value_t::table: { auto table = toml::get(t); @@ -100,7 +100,7 @@ static void prim_fromTOML(EvalState & state, const PosIdx pos, Value ** args, Va for (auto & elem : table) { forceNoNullByte(elem.first); - self(self, attrs.alloc(elem.first), elem.second); + visit(attrs.alloc(elem.first), elem.second); } v.mkAttrs(attrs); @@ -110,7 +110,7 @@ static void prim_fromTOML(EvalState & state, const PosIdx pos, Value ** args, Va auto list = state.buildList(array.size()); for (const auto & [n, v] : enumerate(list)) - self(self, *(v = state.allocValue()), array[n]); + visit(*(v = state.allocValue()), array[n]); v.mkList(list); } break; case toml::value_t::boolean: @@ -155,7 +155,6 @@ static void prim_fromTOML(EvalState & state, const PosIdx pos, Value ** args, Va try { visit( - visit, val, toml::parse( tomlStream, diff --git a/src/libflake/flake.cc b/src/libflake/flake.cc index 8e7e2be26..1a55d4720 100644 --- a/src/libflake/flake.cc +++ b/src/libflake/flake.cc @@ -469,41 +469,31 @@ lockFlake(const Settings & settings, EvalState & state, const FlakeRef & topRef, std::vector parents; - std::function node, - const InputAttrPath & inputAttrPathPrefix, - std::shared_ptr oldNode, - const InputAttrPath & followsPrefix, - const SourcePath & sourcePath, - bool trustLock)> - computeLocks; - - computeLocks = [&]( - /* The inputs of this node, either from flake.nix or - flake.lock. */ - const FlakeInputs & flakeInputs, - /* The node whose locks are to be updated.*/ - ref node, - /* The path to this node in the lock file graph. */ - const InputAttrPath & inputAttrPathPrefix, - /* The old node, if any, from which locks can be - copied. */ - std::shared_ptr oldNode, - /* The prefix relative to which 'follows' should be - interpreted. When a node is initially locked, it's - relative to the node's flake; when it's already locked, - it's relative to the root of the lock file. */ - const InputAttrPath & followsPrefix, - /* The source path of this node's flake. */ - const SourcePath & sourcePath, - bool trustLock) { + auto computeLocks = [&](this auto & computeLocks, + /* The inputs of this node, either from flake.nix or + flake.lock. */ + const FlakeInputs & flakeInputs, + /* The node whose locks are to be updated.*/ + ref node, + /* The path to this node in the lock file graph. */ + const InputAttrPath & inputAttrPathPrefix, + /* The old node, if any, from which locks can be + copied. */ + std::shared_ptr oldNode, + /* The prefix relative to which 'follows' should be + interpreted. When a node is initially locked, it's + relative to the node's flake; when it's already locked, + it's relative to the root of the lock file. */ + const InputAttrPath & followsPrefix, + /* The source path of this node's flake. */ + const SourcePath & sourcePath, + bool trustLock) -> void { debug("computing lock file node '%s'", printInputAttrPath(inputAttrPathPrefix)); /* Get the overrides (i.e. attributes of the form 'inputs.nixops.inputs.nixpkgs.url = ...'). */ - std::function addOverrides; - addOverrides = [&](const FlakeInput & input, const InputAttrPath & prefix) { + auto addOverrides = + [&](this auto & addOverrides, const FlakeInput & input, const InputAttrPath & prefix) -> void { for (auto & [idOverride, inputOverride] : input.overrides) { auto inputAttrPath(prefix); inputAttrPath.push_back(idOverride); diff --git a/src/libflake/lockfile.cc b/src/libflake/lockfile.cc index d3dac19c5..34552db3e 100644 --- a/src/libflake/lockfile.cc +++ b/src/libflake/lockfile.cc @@ -149,9 +149,7 @@ LockFile::LockFile(const fetchers::Settings & fetchSettings, std::string_view co std::map> nodeMap; - std::function getInputs; - - getInputs = [&](Node & node, const nlohmann::json & jsonNode) { + auto getInputs = [&](this auto & getInputs, Node & node, const nlohmann::json & jsonNode) -> void { if (jsonNode.find("inputs") == jsonNode.end()) return; for (auto & i : jsonNode["inputs"].items()) { @@ -276,9 +274,7 @@ std::optional LockFile::isUnlocked(const fetchers::Settings & fetchSet { std::set> nodes; - std::function node)> visit; - - visit = [&](ref node) { + auto visit = [&](this auto & visit, ref node) -> void { if (!nodes.insert(node).second) return; for (auto & i : node->inputs) @@ -332,9 +328,7 @@ std::map LockFile::getAllInputs() const std::set> done; std::map res; - std::function node)> recurse; - - recurse = [&](const InputAttrPath & prefix, ref node) { + auto recurse = [&](this auto & recurse, const InputAttrPath & prefix, ref node) -> void { if (!done.insert(node).second) return; diff --git a/src/libstore/derived-path-map.cc b/src/libstore/derived-path-map.cc index bcbdc85bd..ff1668ca9 100644 --- a/src/libstore/derived-path-map.cc +++ b/src/libstore/derived-path-map.cc @@ -6,15 +6,14 @@ namespace nix { template typename DerivedPathMap::ChildNode & DerivedPathMap::ensureSlot(const SingleDerivedPath & k) { - std::function initIter; - initIter = [&](const auto & k) -> auto & { + auto initIter = [&](this auto & initIter, const auto & k) -> ChildNode & { return std::visit( overloaded{ - [&](const SingleDerivedPath::Opaque & bo) -> auto & { + [&](const SingleDerivedPath::Opaque & bo) -> ChildNode & { // will not overwrite if already there return map[bo.path]; }, - [&](const SingleDerivedPath::Built & bfd) -> auto & { + [&](const SingleDerivedPath::Built & bfd) -> ChildNode & { auto & n = initIter(*bfd.drvPath); return n.childMap[bfd.output]; }, @@ -27,15 +26,14 @@ typename DerivedPathMap::ChildNode & DerivedPathMap::ensureSlot(const Sing template typename DerivedPathMap::ChildNode * DerivedPathMap::findSlot(const SingleDerivedPath & k) { - std::function initIter; - initIter = [&](const auto & k) { + auto initIter = [&](this auto & initIter, const auto & k) -> ChildNode * { return std::visit( overloaded{ - [&](const SingleDerivedPath::Opaque & bo) { + [&](const SingleDerivedPath::Opaque & bo) -> ChildNode * { auto it = map.find(bo.path); return it != map.end() ? &it->second : nullptr; }, - [&](const SingleDerivedPath::Built & bfd) { + [&](const SingleDerivedPath::Built & bfd) -> ChildNode * { auto * n = initIter(*bfd.drvPath); if (!n) return (ChildNode *) nullptr; diff --git a/src/libstore/nar-accessor.cc b/src/libstore/nar-accessor.cc index f0882d52d..7e118ddc4 100644 --- a/src/libstore/nar-accessor.cc +++ b/src/libstore/nar-accessor.cc @@ -146,9 +146,7 @@ struct NarAccessor : public SourceAccessor { using json = nlohmann::json; - std::function recurse; - - recurse = [&](NarMember & member, const json & v) { + auto recurse = [&](this auto & recurse, NarMember & member, const json & v) -> void { std::string type = v["type"]; if (type == "directory") { diff --git a/src/libutil-tests/git.cc b/src/libutil-tests/git.cc index 6180a4cfc..f21a70ebb 100644 --- a/src/libutil-tests/git.cc +++ b/src/libutil-tests/git.cc @@ -284,8 +284,7 @@ TEST_F(GitTest, both_roundrip) MemorySink sinkFiles2{*files2}; - std::function mkSinkHook; - mkSinkHook = [&](auto prefix, auto & hash, auto blobMode) { + auto mkSinkHook = [&](this auto & mkSinkHook, auto prefix, auto & hash, auto blobMode) -> void { StringSource in{cas[hash]}; parse( sinkFiles2, diff --git a/src/libutil/include/nix/util/topo-sort.hh b/src/libutil/include/nix/util/topo-sort.hh index 9f403e2e6..b6862e8e9 100644 --- a/src/libutil/include/nix/util/topo-sort.hh +++ b/src/libutil/include/nix/util/topo-sort.hh @@ -14,9 +14,7 @@ std::vector topoSort( std::vector sorted; decltype(items) visited, parents; - std::function dfsVisit; - - dfsVisit = [&](const T & path, const T * parent) { + auto dfsVisit = [&](this auto & dfsVisit, const T & path, const T * parent) -> void { if (parents.count(path)) throw makeCycleError(path, *parent); diff --git a/src/nix/eval.cc b/src/nix/eval.cc index 10d0a1841..401e7bf36 100644 --- a/src/nix/eval.cc +++ b/src/nix/eval.cc @@ -85,9 +85,8 @@ struct CmdEval : MixJSON, InstallableValueCommand, MixReadOnlyOption if (pathExists(*writeTo)) throw Error("path '%s' already exists", writeTo->string()); - std::function recurse; - - recurse = [&](Value & v, const PosIdx pos, const std::filesystem::path & path) { + auto recurse = + [&](this auto & recurse, Value & v, const PosIdx pos, const std::filesystem::path & path) -> void { state->forceValue(v, pos); if (v.type() == nString) // FIXME: disallow strings with contexts? diff --git a/src/nix/flake-prefetch-inputs.cc b/src/nix/flake-prefetch-inputs.cc index 096eaf539..34b98f5ee 100644 --- a/src/nix/flake-prefetch-inputs.cc +++ b/src/nix/flake-prefetch-inputs.cc @@ -38,8 +38,7 @@ struct CmdFlakePrefetchInputs : FlakeCommand std::atomic nrFailed{0}; - std::function visit; - visit = [&](const Node & node) { + auto visit = [&](this auto & visit, const Node & node) -> void { if (!state_.lock()->done.insert(&node).second) return; diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 04d4ec8eb..63e1e6323 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -269,9 +269,7 @@ struct CmdFlakeMetadata : FlakeCommand, MixJSON std::set> visited; - std::function recurse; - - recurse = [&](const Node & node, const std::string & prefix) { + auto recurse = [&](this auto & recurse, const Node & node, const std::string & prefix) -> void { for (const auto & [i, input] : enumerate(node.inputs)) { bool last = i + 1 == node.inputs.size(); diff --git a/src/nix/search.cc b/src/nix/search.cc index 20bb4cd5d..293b20549 100644 --- a/src/nix/search.cc +++ b/src/nix/search.cc @@ -90,10 +90,10 @@ struct CmdSearch : InstallableValueCommand, MixJSON uint64_t results = 0; - std::function & attrPath, bool initialRecurse)> - visit; - - visit = [&](eval_cache::AttrCursor & cursor, const std::vector & attrPath, bool initialRecurse) { + auto visit = [&](this auto & visit, + eval_cache::AttrCursor & cursor, + const std::vector & attrPath, + bool initialRecurse) -> void { auto attrPathS = state->symbols.resolve(attrPath); Activity act(*logger, lvlInfo, actUnknown, fmt("evaluating '%s'", concatStringsSep(".", attrPathS))); diff --git a/src/nix/why-depends.cc b/src/nix/why-depends.cc index dc30fabd7..91be53724 100644 --- a/src/nix/why-depends.cc +++ b/src/nix/why-depends.cc @@ -160,16 +160,15 @@ struct CmdWhyDepends : SourceExprCommand, MixOperateOnOptions } } + struct BailOut + {}; + /* Print the subgraph of nodes that have 'dependency' in their closure (i.e., that have a non-infinite distance to 'dependency'). Print every edge on a path between `package` and `dependency`. */ - std::function printNode; - - struct BailOut - {}; - - printNode = [&](Node & node, const std::string & firstPad, const std::string & tailPad) { + auto printNode = + [&](this auto & printNode, Node & node, const std::string & firstPad, const std::string & tailPad) -> void { assert(node.dist != inf); if (precise) { logger->cout(