From 61eed2c97c6cdeb04a8673f56c00fee4fc68ea66 Mon Sep 17 00:00:00 2001 From: eldritch horrors Date: Tue, 3 Dec 2024 20:38:41 +0100 Subject: [PATCH] libexpr: fully separate evaluator and running evaluation this finally gives us a witness type we can use to prove that a certain call graph subtree can't be used in kj promises using only a single new assumption: if EvalState& is never held as a reference member of a type and instead only ever passes as an argument or held on the stack we can be certain that anything that has access to en EvalState ref must never be run inside a promise and, crucially, that anything that doesn't have access to an EvalState& *can* be run inside a promise without problems. Change-Id: I6c15ada479175ad7e6cd3e4a729a5586b3ba30d6 --- lix/legacy/nix-build.cc | 4 +- lix/legacy/nix-env.cc | 20 +-- lix/legacy/nix-instantiate.cc | 4 +- lix/libcmd/command.cc | 8 +- lix/libcmd/command.hh | 11 +- lix/libcmd/common-eval-args.cc | 4 +- lix/libcmd/common-eval-args.hh | 5 +- lix/libcmd/installable-attr-path.cc | 4 +- lix/libcmd/installable-attr-path.hh | 4 +- lix/libcmd/installable-flake.cc | 2 +- lix/libcmd/installable-flake.hh | 5 +- lix/libcmd/installable-value.hh | 10 +- lix/libcmd/installables.cc | 76 +++++----- lix/libcmd/repl.cc | 4 +- lix/libexpr/eval-cache.cc | 2 +- lix/libexpr/eval-cache.hh | 4 +- lix/libexpr/eval.cc | 43 +++--- lix/libexpr/eval.hh | 153 ++++++++++++-------- lix/libexpr/flake/flake.cc | 8 +- lix/libexpr/flake/flake.hh | 3 +- lix/libexpr/nixexpr.cc | 42 +++--- lix/libexpr/nixexpr.hh | 14 +- lix/libexpr/parser/parser.cc | 2 +- lix/libexpr/primops/fetchTree.cc | 4 +- lix/nix/build.cc | 2 +- lix/nix/bundle.cc | 4 +- lix/nix/derivation-show.cc | 2 +- lix/nix/develop.cc | 30 ++-- lix/nix/diff-closures.cc | 2 +- lix/nix/edit.cc | 4 +- lix/nix/eval.cc | 4 +- lix/nix/flake.cc | 33 +++-- lix/nix/fmt.cc | 7 +- lix/nix/log.cc | 2 +- lix/nix/main.cc | 6 +- lix/nix/prefetch.cc | 5 +- lix/nix/profile.cc | 20 +-- lix/nix/repl.cc | 4 +- lix/nix/run.cc | 4 +- lix/nix/search.cc | 4 +- lix/nix/store-copy-log.cc | 2 +- lix/nix/upgrade-nix.cc | 8 +- lix/nix/why-depends.cc | 2 +- tests/unit/libcmd/args.cc | 2 +- tests/unit/libexpr-support/tests/libexpr.hh | 6 +- 45 files changed, 317 insertions(+), 272 deletions(-) diff --git a/lix/legacy/nix-build.cc b/lix/legacy/nix-build.cc index 0929033fb..0642a995b 100644 --- a/lix/legacy/nix-build.cc +++ b/lix/legacy/nix-build.cc @@ -198,9 +198,9 @@ static void main_nix_build(std::string programName, Strings argv) auto store = openStore(); auto evalStore = myArgs.evalStoreUrl ? openStore(*myArgs.evalStoreUrl) : store; - auto evaluator = std::make_unique(myArgs.searchPath, evalStore, store); + auto evaluator = std::make_unique(myArgs.searchPath, evalStore, store); evaluator->repair = myArgs.repair; - auto & state = evaluator; + auto state = evaluator->begin(); if (myArgs.repair) buildMode = bmRepair; auto autoArgs = myArgs.getAutoArgs(*evaluator); diff --git a/lix/legacy/nix-env.cc b/lix/legacy/nix-env.cc index 053e5d09b..58353fc32 100644 --- a/lix/legacy/nix-env.cc +++ b/lix/legacy/nix-env.cc @@ -59,7 +59,7 @@ struct Globals { InstallSourceInfo instSource; Path profile; - std::shared_ptr state; + std::shared_ptr state; bool dryRun; bool preserveInstalled; bool removeAll; @@ -107,7 +107,7 @@ static bool isNixExpr(const SourcePath & path, struct InputAccessor::Stat & st) static constexpr size_t maxAttrs = 1024; -static void getAllExprs(EvalState & state, +static void getAllExprs(Evaluator & state, const SourcePath & path, StringSet & seen, BindingsBuilder & attrs) { StringSet namesSorted; @@ -177,7 +177,7 @@ static void loadSourceExpr(EvalState & state, const SourcePath & path, Value & v auto attrs = state.ctx.buildBindings(maxAttrs); attrs.alloc("_combineChannels").mkList(0); StringSet seen; - getAllExprs(state, path, seen, attrs); + getAllExprs(state.ctx, path, seen, attrs); v.mkAttrs(attrs); } @@ -511,7 +511,7 @@ static void installDerivations(Globals & globals, { debug("installing derivations"); - auto state = globals.state; + auto state = globals.state->begin(); /* Get the set of user environment elements to be installed. */ DrvInfos newElems, newElemsTmp; @@ -592,7 +592,7 @@ static void upgradeDerivations(Globals & globals, { debug("upgrading derivations"); - auto state = globals.state; + auto state = globals.state->begin(); /* Upgrade works as follows: we take all currently installed derivations, and for any derivation matching any selector, look @@ -718,7 +718,7 @@ static void opSetFlag(Globals & globals, Strings opFlags, Strings opArgs) std::string flagValue = *arg++; DrvNames selectors = drvNamesFromArgs(Strings(arg, opArgs.end())); - auto state = globals.state; + auto state = globals.state->begin(); while (true) { std::string lockToken = optimisticLockProfile(globals.profile); @@ -748,7 +748,7 @@ static void opSetFlag(Globals & globals, Strings opFlags, Strings opArgs) static void opSet(Globals & globals, Strings opFlags, Strings opArgs) { - auto state = globals.state; + auto state = globals.state->begin(); auto store2 = globals.state->store.dynamic_pointer_cast(); if (!store2) throw Error("--set is not supported for this Nix store"); @@ -797,7 +797,7 @@ static void opSet(Globals & globals, Strings opFlags, Strings opArgs) static void uninstallDerivations(Globals & globals, Strings & selectors, Path & profile) { - auto state = globals.state; + auto state = globals.state->begin(); while (true) { auto lockToken = optimisticLockProfile(profile); @@ -1003,7 +1003,7 @@ static void queryJSON(EvalState & state, Globals & globals, std::vector static void opQuery(Globals & globals, Strings opFlags, Strings opArgs) { auto & store { *globals.state->store }; - auto state = globals.state; + auto state = globals.state->begin(); Strings remaining; std::string attrPath; @@ -1533,7 +1533,7 @@ static int main_nix_env(std::string programName, Strings argv) auto store = openStore(); - globals.state = std::shared_ptr(new EvalState(myArgs.searchPath, store)); + globals.state = std::make_shared(myArgs.searchPath, store); globals.state->repair = myArgs.repair; globals.instSource.nixExprPath = std::make_shared( diff --git a/lix/legacy/nix-instantiate.cc b/lix/legacy/nix-instantiate.cc index 629c06eb4..201de62e4 100644 --- a/lix/legacy/nix-instantiate.cc +++ b/lix/legacy/nix-instantiate.cc @@ -157,8 +157,8 @@ static int main_nix_instantiate(std::string programName, Strings argv) auto store = openStore(); auto evalStore = myArgs.evalStoreUrl ? openStore(*myArgs.evalStoreUrl) : store; - auto evaluator = std::make_unique(myArgs.searchPath, evalStore, store); - auto & state = evaluator; + auto evaluator = std::make_unique(myArgs.searchPath, evalStore, store); + auto state = evaluator->begin(); evaluator->repair = myArgs.repair; Bindings & autoArgs = *myArgs.getAutoArgs(*evaluator); diff --git a/lix/libcmd/command.cc b/lix/libcmd/command.cc index 5e12cc4dc..90739101d 100644 --- a/lix/libcmd/command.cc +++ b/lix/libcmd/command.cc @@ -102,17 +102,17 @@ ref EvalCommand::getEvalStore() return ref(evalStore); } -ref EvalCommand::getEvalState() +ref EvalCommand::getEvaluator() { if (!evalState) { - evalState = std::allocate_shared( + evalState = std::allocate_shared( TraceableAllocator(), searchPath, getEvalStore(), getStore(), startReplOnEvalErrors ? AbstractNixRepl::runSimple : nullptr ); evalState->repair = repair; } - return ref(evalState); + return ref(evalState); } MixOperateOnOptions::MixOperateOnOptions() @@ -162,7 +162,7 @@ void BuiltPathsCommand::run(ref store, Installables && installables) for (auto & p : store->queryAllValidPaths()) paths.emplace_back(BuiltPath::Opaque{p}); } else { - paths = Installable::toBuiltPaths(*getEvalState(), getEvalStore(), store, realiseMode, operateOn, installables); + paths = Installable::toBuiltPaths(*getEvaluator()->begin(), getEvalStore(), store, realiseMode, operateOn, installables); if (recursive) { // XXX: This only computes the store path closure, ignoring // intermediate realisations diff --git a/lix/libcmd/command.hh b/lix/libcmd/command.hh index 478032116..3a55b1532 100644 --- a/lix/libcmd/command.hh +++ b/lix/libcmd/command.hh @@ -77,12 +77,12 @@ struct EvalCommand : virtual StoreCommand, MixEvalArgs ref getEvalStore(); - ref getEvalState(); + ref getEvaluator(); private: std::shared_ptr evalStore; - std::shared_ptr evalState; + std::shared_ptr evalState; }; /** @@ -127,7 +127,7 @@ struct SourceExprCommand : virtual Args, MixFlakeOptions /** * Complete an installable from the given prefix. */ - void completeInstallable(AddCompletions & completions, std::string_view prefix); + void completeInstallable(EvalState & state, AddCompletions & completions, std::string_view prefix); /** * Convenience wrapper around the underlying function to make setting the @@ -324,7 +324,7 @@ struct MixEnvironment : virtual Args { void completeFlakeInputPath( AddCompletions & completions, - ref evalState, + EvalState & evalState, const std::vector & flakeRefs, std::string_view prefix); @@ -332,7 +332,8 @@ void completeFlakeRef(AddCompletions & completions, ref store, std::strin void completeFlakeRefWithFragment( AddCompletions & completions, - ref evalState, + EvalState & evalState, + ref evaluator, flake::LockFlags lockFlags, Strings attrPathPrefixes, const Strings & defaultFlakeAttrPaths, diff --git a/lix/libcmd/common-eval-args.cc b/lix/libcmd/common-eval-args.cc index 4cf6b43af..26e0380c0 100644 --- a/lix/libcmd/common-eval-args.cc +++ b/lix/libcmd/common-eval-args.cc @@ -171,7 +171,7 @@ MixEvalArgs::MixEvalArgs() }); } -Bindings * MixEvalArgs::getAutoArgs(EvalState & state) +Bindings * MixEvalArgs::getAutoArgs(Evaluator & state) { auto res = state.buildBindings(autoArgs.size()); for (auto & i : autoArgs) { @@ -187,7 +187,7 @@ Bindings * MixEvalArgs::getAutoArgs(EvalState & state) return res.finish(); } -SourcePath lookupFileArg(EvalState & state, std::string_view fileArg) +SourcePath lookupFileArg(Evaluator & state, std::string_view fileArg) { if (EvalSettings::isPseudoUrl(fileArg)) { auto const url = EvalSettings::resolvePseudoUrl(fileArg); diff --git a/lix/libcmd/common-eval-args.hh b/lix/libcmd/common-eval-args.hh index 51ca70b19..1d42ee5e4 100644 --- a/lix/libcmd/common-eval-args.hh +++ b/lix/libcmd/common-eval-args.hh @@ -1,6 +1,7 @@ #pragma once ///@file +#include "lix/libexpr/eval.hh" #include "lix/libutil/args.hh" #include "lix/libmain/common-args.hh" #include "lix/libexpr/search-path.hh" @@ -18,7 +19,7 @@ struct MixEvalArgs : virtual Args, virtual MixRepair MixEvalArgs(); - Bindings * getAutoArgs(EvalState & state); + Bindings * getAutoArgs(Evaluator & state); SearchPath searchPath; @@ -48,6 +49,6 @@ private: * * @exception nix::ThrownError for failed search path lookup. Probably others. */ -SourcePath lookupFileArg(EvalState & state, std::string_view fileArg); +SourcePath lookupFileArg(Evaluator & state, std::string_view fileArg); } diff --git a/lix/libcmd/installable-attr-path.cc b/lix/libcmd/installable-attr-path.cc index 28bcb351b..11035206c 100644 --- a/lix/libcmd/installable-attr-path.cc +++ b/lix/libcmd/installable-attr-path.cc @@ -12,7 +12,7 @@ namespace nix { InstallableAttrPath::InstallableAttrPath( - ref state, + ref state, SourceExprCommand & cmd, Value * v, const std::string & attrPath, @@ -92,7 +92,7 @@ DerivedPathsWithInfo InstallableAttrPath::toDerivedPaths(EvalState & state) } InstallableAttrPath InstallableAttrPath::parse( - ref state, + ref state, SourceExprCommand & cmd, Value * v, std::string_view prefix, diff --git a/lix/libcmd/installable-attr-path.hh b/lix/libcmd/installable-attr-path.hh index d040c634f..ce76307e6 100644 --- a/lix/libcmd/installable-attr-path.hh +++ b/lix/libcmd/installable-attr-path.hh @@ -20,7 +20,7 @@ class InstallableAttrPath : public InstallableValue ExtendedOutputsSpec extendedOutputsSpec; InstallableAttrPath( - ref state, + ref state, SourceExprCommand & cmd, Value * v, const std::string & attrPath, @@ -35,7 +35,7 @@ class InstallableAttrPath : public InstallableValue public: static InstallableAttrPath parse( - ref state, + ref state, SourceExprCommand & cmd, Value * v, std::string_view prefix, diff --git a/lix/libcmd/installable-flake.cc b/lix/libcmd/installable-flake.cc index 5264af0b9..3db1f6031 100644 --- a/lix/libcmd/installable-flake.cc +++ b/lix/libcmd/installable-flake.cc @@ -53,7 +53,7 @@ static std::string showAttrPaths(const std::vector & paths) InstallableFlake::InstallableFlake( SourceExprCommand * cmd, - ref state, + ref state, FlakeRef && flakeRef, std::string_view fragment, ExtendedOutputsSpec extendedOutputsSpec, diff --git a/lix/libcmd/installable-flake.hh b/lix/libcmd/installable-flake.hh index d7be2d93d..a608e702a 100644 --- a/lix/libcmd/installable-flake.hh +++ b/lix/libcmd/installable-flake.hh @@ -2,6 +2,7 @@ ///@file #include "lix/libcmd/installable-value.hh" +#include "lix/libexpr/eval.hh" namespace nix { @@ -40,7 +41,7 @@ struct InstallableFlake : InstallableValue InstallableFlake( SourceExprCommand * cmd, - ref state, + ref state, FlakeRef && flakeRef, std::string_view fragment, ExtendedOutputsSpec extendedOutputsSpec, @@ -81,7 +82,7 @@ static inline FlakeRef defaultNixpkgsFlakeRef() } ref openEvalCache( - eval_cache::CachingEvalState & state, + eval_cache::CachingEvaluator & state, std::shared_ptr lockedFlake); } diff --git a/lix/libcmd/installable-value.hh b/lix/libcmd/installable-value.hh index 66d35ee4e..23dd66901 100644 --- a/lix/libcmd/installable-value.hh +++ b/lix/libcmd/installable-value.hh @@ -3,6 +3,7 @@ #include "lix/libcmd/installables.hh" #include "lix/libexpr/eval-cache.hh" +#include "lix/libexpr/eval.hh" #include "lix/libexpr/flake/flake.hh" namespace nix { @@ -70,14 +71,9 @@ struct ExtraPathInfoValue : ExtraPathInfo */ struct InstallableValue : Installable { - ref evaluator; - ref state; + ref evaluator; - InstallableValue(ref evaluator) - : evaluator(evaluator) - , state(evaluator) - { - } + InstallableValue(ref evaluator) : evaluator(evaluator) {} virtual ~InstallableValue() { } diff --git a/lix/libcmd/installables.cc b/lix/libcmd/installables.cc index 700f5115a..d2d64730d 100644 --- a/lix/libcmd/installables.cc +++ b/lix/libcmd/installables.cc @@ -25,12 +25,12 @@ namespace nix { void completeFlakeInputPath( AddCompletions & completions, - ref evalState, + EvalState & evalState, const std::vector & flakeRefs, std::string_view prefix) { for (auto & flakeRef : flakeRefs) { - auto flake = flake::getFlake(*evalState, flakeRef, true); + auto flake = flake::getFlake(evalState, flakeRef, true); for (auto & input : flake.inputs) if (input.first.starts_with(prefix)) completions.add(input.first); @@ -86,9 +86,9 @@ MixFlakeOptions::MixFlakeOptions() }}, .completer = {[&](AddCompletions & completions, size_t n, std::string_view prefix) { if (n == 0) { - completeFlakeInputPath(completions, getEvalState(), getFlakeRefsForCompletion(), prefix); + completeFlakeInputPath(completions, *getEvaluator()->begin(), getFlakeRefsForCompletion(), prefix); } else if (n == 1) { - completeFlakeRef(completions, getEvalState()->store, prefix); + completeFlakeRef(completions, getEvaluator()->store, prefix); } }} }); @@ -121,7 +121,7 @@ MixFlakeOptions::MixFlakeOptions() .category = category, .labels = {"flake-url"}, .handler = {[&](std::string flakeRef) { - auto evalState = getEvalState(); + auto evalState = getEvaluator()->begin(); auto flake = flake::lockFlake( *evalState, parseFlakeRef(flakeRef, absPath(".")), @@ -137,7 +137,7 @@ MixFlakeOptions::MixFlakeOptions() } }}, .completer = {[&](AddCompletions & completions, size_t, std::string_view prefix) { - completeFlakeRef(completions, getEvalState()->store, prefix); + completeFlakeRef(completions, getEvaluator()->store, prefix); }} }); } @@ -202,28 +202,28 @@ Strings SourceExprCommand::getDefaultFlakeAttrPathPrefixes() Args::CompleterClosure SourceExprCommand::getCompleteInstallable() { return [this](AddCompletions & completions, size_t, std::string_view prefix) { - completeInstallable(completions, prefix); + completeInstallable(*getEvaluator()->begin(), completions, prefix); }; } -void SourceExprCommand::completeInstallable(AddCompletions & completions, std::string_view prefix) +void SourceExprCommand::completeInstallable(EvalState & state, AddCompletions & completions, std::string_view prefix) { try { if (file) { completions.setType(AddCompletions::Type::Attrs); evalSettings.pureEval.override(false); - auto state = getEvalState(); - state->paths.allowedPaths.reset(); + auto evaluator = getEvaluator(); + evaluator->paths.allowedPaths.reset(); - Expr & e = state->parseExprFromFile( - resolveExprPath(state->paths.checkSourcePath(lookupFileArg(*state, *file))) - ); + Expr & e = evaluator->parseExprFromFile( + resolveExprPath(evaluator->paths.checkSourcePath(lookupFileArg(*evaluator, *file))) + ); Value root; - state->eval(e, root); + state.eval(e, root); - auto autoArgs = getAutoArgs(*state); + auto autoArgs = getAutoArgs(*evaluator); std::string prefix_ = std::string(prefix); auto sep = prefix_.rfind('.'); @@ -236,15 +236,15 @@ void SourceExprCommand::completeInstallable(AddCompletions & completions, std::s prefix_ = ""; } - auto [v, pos] = findAlongAttrPath(*state, prefix_, *autoArgs, root); + auto [v, pos] = findAlongAttrPath(state, prefix_, *autoArgs, root); Value &v1(*v); - state->forceValue(v1, pos); + state.forceValue(v1, pos); Value v2; - state->autoCallFunction(*autoArgs, v1, v2); + state.autoCallFunction(*autoArgs, v1, v2); if (v2.type() == nAttrs) { for (auto & i : *v2.attrs) { - std::string name = state->symbols[i.name]; + std::string name = evaluator->symbols[i.name]; if (name.find(searchWord) == 0) { if (prefix_ == "") completions.add(name); @@ -256,7 +256,8 @@ void SourceExprCommand::completeInstallable(AddCompletions & completions, std::s } else { completeFlakeRefWithFragment( completions, - getEvalState(), + state, + getEvaluator(), lockFlags, getDefaultFlakeAttrPathPrefixes(), getDefaultFlakeAttrPaths(), @@ -269,7 +270,8 @@ void SourceExprCommand::completeInstallable(AddCompletions & completions, std::s void completeFlakeRefWithFragment( AddCompletions & completions, - ref evalState, + EvalState & evalState, + ref evaluator, flake::LockFlags lockFlags, Strings attrPathPrefixes, const Strings & defaultFlakeAttrPaths, @@ -280,7 +282,7 @@ void completeFlakeRefWithFragment( try { auto hash = prefix.find('#'); if (hash == std::string::npos) { - completeFlakeRef(completions, evalState->store, prefix); + completeFlakeRef(completions, evaluator->store, prefix); } else { completions.setType(AddCompletions::Type::Attrs); @@ -293,8 +295,10 @@ void completeFlakeRefWithFragment( auto flakeRefS = std::string(prefix.substr(0, hash)); auto flakeRef = parseFlakeRef(expandTilde(flakeRefS), absPath(".")); - auto evalCache = openEvalCache(*evalState, - std::make_shared(lockFlake(*evalState, flakeRef, lockFlags))); + auto evalCache = openEvalCache( + *evaluator, + std::make_shared(lockFlake(evalState, flakeRef, lockFlags)) + ); auto root = evalCache->getRoot(); @@ -317,12 +321,12 @@ void completeFlakeRefWithFragment( attrPath.pop_back(); } - auto attr = root->findAlongAttrPath(*evalState, attrPath); + auto attr = root->findAlongAttrPath(evalState, attrPath); if (!attr) continue; - for (auto & attr2 : (*attr)->getAttrs(*evalState)) { + for (auto & attr2 : (*attr)->getAttrs(evalState)) { if (std::string_view attr2s = attr2; attr2s.starts_with(lastAttr)) { - auto attrPath2 = (*attr)->getAttrPath(*evalState, attr2s); + auto attrPath2 = (*attr)->getAttrPath(evalState, attr2s); /* Strip the attrpath prefix. */ attrPath2.erase(attrPath2.begin(), attrPath2.begin() + attrPathPrefix.size()); completions.add(flakeRefS + "#" + prefixRoot + concatStringsSep(".", attrPath2)); @@ -334,7 +338,7 @@ void completeFlakeRefWithFragment( attrpaths. */ if (fragment.empty()) { for (auto & attrPath : defaultFlakeAttrPaths) { - auto attr = root->findAlongAttrPath(*evalState, parseAttrPath(attrPath)); + auto attr = root->findAlongAttrPath(evalState, parseAttrPath(attrPath)); if (!attr) continue; completions.add(flakeRefS + "#" + prefixRoot); } @@ -392,7 +396,7 @@ static StorePath getDeriver( } ref openEvalCache( - eval_cache::CachingEvalState & state, + eval_cache::CachingEvaluator & state, std::shared_ptr lockedFlake) { auto fingerprint = evalSettings.useEvalCache && evalSettings.pureEval @@ -435,10 +439,10 @@ Installables SourceExprCommand::parseInstallables( // FIXME: backward compatibility hack if (file) { evalSettings.pureEval.override(false); - getEvalState()->paths.allowedPaths.reset(); + getEvaluator()->paths.allowedPaths.reset(); } - auto evaluator = getEvalState(); + auto evaluator = getEvaluator(); auto vFile = evaluator->mem.allocValue(); if (file == "-") { @@ -446,9 +450,9 @@ Installables SourceExprCommand::parseInstallables( state.eval(e, *vFile); } else if (file) - state.evalFile(lookupFileArg(state, *file), *vFile); + state.evalFile(lookupFileArg(*evaluator, *file), *vFile); else { - auto & e = state.parseExprFromString(*expr, CanonPath::fromCwd()); + auto & e = evaluator->parseExprFromString(*expr, CanonPath::fromCwd()); state.eval(e, *vFile); } @@ -486,7 +490,7 @@ Installables SourceExprCommand::parseInstallables( auto [flakeRef, fragment] = parseFlakeRefWithFragment(std::string { prefix }, absPath(".")); result.push_back(make_ref( this, - getEvalState(), + getEvaluator(), std::move(flakeRef), fragment, std::move(extendedOutputsSpec), @@ -836,7 +840,7 @@ std::vector InstallableCommand::getFlakeRefsForCompletion() void InstallablesCommand::run(ref store, std::vector && rawInstallables) { - auto installables = parseInstallables(*getEvalState(), store, rawInstallables); + auto installables = parseInstallables(*getEvaluator()->begin(), store, rawInstallables); run(store, std::move(installables)); } @@ -853,7 +857,7 @@ InstallableCommand::InstallableCommand() void InstallableCommand::run(ref store) { - auto installable = parseInstallable(*getEvalState(), store, _installable); + auto installable = parseInstallable(*getEvaluator()->begin(), store, _installable); run(store, std::move(installable)); } diff --git a/lix/libcmd/repl.cc b/lix/libcmd/repl.cc index 55e5229db..56f9387ec 100644 --- a/lix/libcmd/repl.cc +++ b/lix/libcmd/repl.cc @@ -119,7 +119,7 @@ struct NixRepl }; /* clang-format: on */ - EvalState & evaluator; + Evaluator & evaluator; size_t debugTraceIndex; Strings loadedFiles; @@ -232,7 +232,7 @@ static box_ptr makeInteracter() { NixRepl::NixRepl(const SearchPath & searchPath, nix::ref store, EvalState & state, std::function getValues) : AbstractNixRepl(state) - , evaluator(state) + , evaluator(state.ctx) , debugTraceIndex(0) , getValues(getValues) , staticEnv(new StaticEnv(nullptr, evaluator.builtins.staticEnv.get())) diff --git a/lix/libexpr/eval-cache.cc b/lix/libexpr/eval-cache.cc index fb414a509..1287c5e4d 100644 --- a/lix/libexpr/eval-cache.cc +++ b/lix/libexpr/eval-cache.cc @@ -322,7 +322,7 @@ static std::shared_ptr makeAttrDb(const Hash & fingerprint) } } -ref CachingEvalState::getCacheFor(Hash hash, RootLoader rootLoader) +ref CachingEvaluator::getCacheFor(Hash hash, RootLoader rootLoader) { if (auto it = caches.find(hash); it != caches.end()) { return it->second; diff --git a/lix/libexpr/eval-cache.hh b/lix/libexpr/eval-cache.hh index b5fdf97ca..bd6ed0128 100644 --- a/lix/libexpr/eval-cache.hh +++ b/lix/libexpr/eval-cache.hh @@ -22,7 +22,7 @@ typedef std::function RootLoader; * the rather strong connection between EvalState and these caches. At some * future time the cache interface should be changed to hide its EvalState. */ -class CachingEvalState : public EvalState +class CachingEvaluator : public Evaluator { /** * A cache for evaluation caches, so as to reuse the same root value if possible @@ -30,7 +30,7 @@ class CachingEvalState : public EvalState std::map> caches; public: - using EvalState::EvalState; + using Evaluator::Evaluator; ref getCacheFor(Hash hash, RootLoader rootLoader); }; diff --git a/lix/libexpr/eval.cc b/lix/libexpr/eval.cc index 20a368dba..3e1963350 100644 --- a/lix/libexpr/eval.cc +++ b/lix/libexpr/eval.cc @@ -305,8 +305,7 @@ EvalPaths::EvalPaths( } } -EvalContext::EvalContext( - EvalState & parent, +Evaluator::Evaluator( const SearchPath & _searchPath, ref store, std::shared_ptr buildStore, @@ -330,8 +329,11 @@ EvalContext::EvalContext( debugRepl ? std::make_unique( positions, symbols, - [&parent, debugRepl](const ValMap & extraEnv) { return debugRepl(parent, extraEnv); } - ) + [this, debugRepl](const ValMap & extraEnv) { + return activeEval + ? debugRepl(*activeEval, extraEnv) + : ReplExitStatus::Continue; + }) : nullptr } , errors{positions, debug.get()} @@ -341,17 +343,20 @@ EvalContext::EvalContext( static_assert(sizeof(Env) <= 16, "environment must be <= 16 bytes"); } -EvalState::EvalState( - const SearchPath & _searchPath, - ref store, - std::shared_ptr buildStore, - std::function debugRepl) - : EvalContext(*this, _searchPath, store, buildStore, debugRepl) +box_ptr Evaluator::begin() { + assert(!activeEval); + return box_ptr::unsafeFromNonnull(std::unique_ptr(new EvalState(*this))); +} + +EvalState::EvalState(Evaluator & ctx) : ctx(ctx) +{ + ctx.activeEval = this; } EvalState::~EvalState() { + ctx.activeEval = nullptr; } @@ -788,7 +793,7 @@ Value EvalMemory::newList(size_t size) } -void EvalState::evalLazily(Expr & e, Value & v) +void Evaluator::evalLazily(Expr & e, Value & v) { v.mkThunk(&builtins.env, e); stats.nrThunks++; @@ -2531,7 +2536,7 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v } } -bool EvalState::fullGC() { +bool Evaluator::fullGC() { #if HAVE_BOEHMGC GC_gcollect(); // Check that it ran. We might replace this with a version that uses more @@ -2545,7 +2550,7 @@ bool EvalState::fullGC() { #endif } -void EvalState::maybePrintStats() +void Evaluator::maybePrintStats() { bool showStats = getEnv("NIX_SHOW_STATS").value_or("0") != "0"; @@ -2560,7 +2565,7 @@ void EvalState::maybePrintStats() } } -void EvalState::printStatistics() +void Evaluator::printStatistics() { struct rusage buf; getrusage(RUSAGE_SELF, &buf); @@ -2700,20 +2705,20 @@ SourcePath resolveExprPath(SourcePath path) } -Expr & EvalState::parseExprFromFile(const SourcePath & path) +Expr & Evaluator::parseExprFromFile(const SourcePath & path) { return parseExprFromFile(path, builtins.staticEnv); } -Expr & EvalState::parseExprFromFile(const SourcePath & path, std::shared_ptr & staticEnv) +Expr & Evaluator::parseExprFromFile(const SourcePath & path, std::shared_ptr & staticEnv) { auto buffer = path.readFile(); return *parse(buffer.data(), buffer.size(), Pos::Origin(path), path.parent(), staticEnv); } -Expr & EvalState::parseExprFromString( +Expr & Evaluator::parseExprFromString( std::string s_, const SourcePath & basePath, std::shared_ptr & staticEnv, @@ -2725,7 +2730,7 @@ Expr & EvalState::parseExprFromString( } -Expr & EvalState::parseExprFromString( +Expr & Evaluator::parseExprFromString( std::string s, const SourcePath & basePath, const FeatureSettings & featureSettings @@ -2735,7 +2740,7 @@ Expr & EvalState::parseExprFromString( } -Expr & EvalState::parseStdin() +Expr & Evaluator::parseStdin() { //Activity act(*logger, lvlTalkative, "parsing standard input"); auto s = make_ref(drainFD(0)); diff --git a/lix/libexpr/eval.hh b/lix/libexpr/eval.hh index c05d9a120..cadc99032 100644 --- a/lix/libexpr/eval.hh +++ b/lix/libexpr/eval.hh @@ -4,6 +4,7 @@ #include "lix/libexpr/attr-set.hh" #include "lix/libexpr/eval-error.hh" #include "lix/libexpr/gc-alloc.hh" +#include "lix/libutil/box_ptr.hh" #include "lix/libutil/generator.hh" #include "lix/libutil/types.hh" #include "lix/libexpr/value.hh" @@ -484,8 +485,13 @@ struct EvalStatistics void addCall(ExprLambda & fun); }; -class EvalContext +class Evaluator { + friend class EvalBuiltins; + friend class EvalState; + + EvalState * activeEval = nullptr; + public: SymbolTable symbols; PosTable positions; @@ -510,35 +516,17 @@ public: std::unique_ptr debug; EvalErrorContext errors; - EvalContext( - EvalState & parent, + Evaluator( const SearchPath & _searchPath, ref store, std::shared_ptr buildStore = nullptr, std::function debugRepl = nullptr ); - EvalContext(const EvalContext &) = delete; - EvalContext(EvalContext &&) = delete; - EvalContext & operator=(const EvalContext &) = delete; - EvalContext & operator=(EvalContext &&) = delete; -}; - - -class EvalState : public EvalContext -{ - friend class EvalBuiltins; - -public: - EvalState & ctx{*this}; - - EvalState( - const SearchPath & _searchPath, - ref store, - std::shared_ptr buildStore = nullptr, - std::function debugRepl = nullptr - ); - ~EvalState(); + Evaluator(const Evaluator &) = delete; + Evaluator(Evaluator &&) = delete; + Evaluator & operator=(const Evaluator &) = delete; + Evaluator & operator=(Evaluator &&) = delete; /** * Parse a Nix expression from the specified file. @@ -563,6 +551,84 @@ public: Expr & parseStdin(); + /** + * Creates a thunk that will evaluate the given expression when forced. + */ + void evalLazily(Expr & e, Value & v); + +private: + Expr * parse( + char * text, + size_t length, + Pos::Origin origin, + const SourcePath & basePath, + std::shared_ptr & staticEnv, + const FeatureSettings & xpSettings = featureSettings); + +public: + BindingsBuilder buildBindings(size_t capacity) + { + return mem.buildBindings(symbols, capacity); + } + + /** + * Print statistics, if enabled. + * + * Performs a full memory GC before printing the statistics, so that the + * GC statistics are more accurate. + */ + void maybePrintStats(); + + /** + * Print statistics, unconditionally, cheaply, without performing a GC first. + */ + void printStatistics(); + + /** + * Perform a full memory garbage collection - not incremental. + * + * @return true if Nix was built with GC and a GC was performed, false if not. + * The return value is currently not thread safe - just the return value. + */ + bool fullGC(); + + /** + * Create an `EvalState` in prepation to evaluate some amount of Nix code. + * + * While preparation of evaluation can be done with Evaluator itself only, + * actually evaluating things requires an EvalState. This function creates + * an EvalState and returns it. At most one EvalState per Evaluator may be + * live at any given point, and references to this EvalState must not live + * anywhere except in the returned box, local variables, or arguments. Any + * reference held in an object type is illegal, be it in a lambda capture, + * a pointer member of an object, a hidden member such as arguments passed + * to functions by `std::thread` or `std::async`—all references held where + * they could be copied are moved from are disallowed. EvalState is thus a + * witness type that a given thread may evaluate nix code and must *never* + * be run inside `kj::Promise` context. This is due to a kj limitation, in + * which it is not possible to block on a promise while already running in + * a promise without doing this blocking on a different event loop/thread. + */ + box_ptr begin(); +}; + + +class EvalState +{ + friend class Evaluator; + + explicit EvalState(Evaluator & ctx); + +public: + Evaluator & ctx; + + EvalState(const EvalState &) = delete; + EvalState(EvalState &&) = delete; + EvalState & operator=(const EvalState &) = delete; + EvalState & operator=(EvalState &&) = delete; + + ~EvalState(); + /** * Evaluate an expression read from the given file to normal form. */ @@ -577,11 +643,6 @@ public: */ void eval(Expr & e, Value & v); - /** - * Creates a thunk that will evaluate the given expression when forced. - */ - void evalLazily(Expr & e, Value & v); - /** * Evaluation the expression, then verify that it has the expected * type. @@ -690,14 +751,6 @@ private: friend struct ExprAttrs; friend struct ExprLet; - Expr * parse( - char * text, - size_t length, - Pos::Origin origin, - const SourcePath & basePath, - std::shared_ptr & staticEnv, - const FeatureSettings & xpSettings = featureSettings); - /** * Current Nix call stack depth, used with `max-call-depth` setting to throw stack overflow hopefully before we run out of system stack. */ @@ -728,11 +781,6 @@ public: */ void autoCallFunction(Bindings & args, Value & fun, Value & res); - BindingsBuilder buildBindings(size_t capacity) - { - return ctx.mem.buildBindings(ctx.symbols, capacity); - } - void mkPos(Value & v, PosIdx pos); /** @@ -772,27 +820,6 @@ public: void concatLists(Value & v, size_t nrLists, Value * * lists, const PosIdx pos, std::string_view errorCtx); - /** - * Print statistics, if enabled. - * - * Performs a full memory GC before printing the statistics, so that the - * GC statistics are more accurate. - */ - void maybePrintStats(); - - /** - * Print statistics, unconditionally, cheaply, without performing a GC first. - */ - void printStatistics(); - - /** - * Perform a full memory garbage collection - not incremental. - * - * @return true if Nix was built with GC and a GC was performed, false if not. - * The return value is currently not thread safe - just the return value. - */ - bool fullGC(); - private: /** diff --git a/lix/libexpr/flake/flake.cc b/lix/libexpr/flake/flake.cc index 199e7b082..8ccdf7373 100644 --- a/lix/libexpr/flake/flake.cc +++ b/lix/libexpr/flake/flake.cc @@ -37,7 +37,7 @@ static std::optional lookupInFlakeCache( } static std::tuple fetchOrSubstituteTree( - EvalState & state, + Evaluator & state, const FlakeRef & originalRef, bool allowLookup, FlakeCache & flakeCache) @@ -217,7 +217,7 @@ static Flake getFlake( InputPath lockRootPath) { auto [sourceInfo, resolvedRef, lockedRef] = fetchOrSubstituteTree( - state, originalRef, allowLookup, flakeCache); + state.ctx, originalRef, allowLookup, flakeCache); // We need to guard against symlink attacks, but before we start doing // filesystem operations we should make sure there's a flake.nix in the @@ -634,7 +634,7 @@ LockedFlake lockFlake( else { auto [sourceInfo, resolvedRef, lockedRef] = fetchOrSubstituteTree( - state, *input.ref, useRegistries, flakeCache); + state.ctx, *input.ref, useRegistries, flakeCache); auto childNode = make_ref(lockedRef, ref, false); @@ -782,7 +782,7 @@ void callFlake(EvalState & state, vLocks->mkString(lockedFlake.lockFile.to_string()); emitTreeAttrs( - state, + state.ctx, *lockedFlake.flake.sourceInfo, lockedFlake.flake.lockedRef.input, *vRootSrc, diff --git a/lix/libexpr/flake/flake.hh b/lix/libexpr/flake/flake.hh index b978a7ad2..b61b3fb62 100644 --- a/lix/libexpr/flake/flake.hh +++ b/lix/libexpr/flake/flake.hh @@ -1,6 +1,7 @@ #pragma once ///@file +#include "lix/libexpr/eval.hh" #include "lix/libutil/types.hh" #include "lix/libexpr/flake/flakeref.hh" #include "lix/libexpr/flake/lockfile.hh" @@ -192,7 +193,7 @@ void callFlake( } void emitTreeAttrs( - EvalState & state, + Evaluator & state, const fetchers::Tree & tree, const fetchers::Input & input, Value & v, diff --git a/lix/libexpr/nixexpr.cc b/lix/libexpr/nixexpr.cc index 376b03f04..1f51fbf45 100644 --- a/lix/libexpr/nixexpr.cc +++ b/lix/libexpr/nixexpr.cc @@ -295,36 +295,36 @@ std::string showAttrPath(const SymbolTable & symbols, const AttrPath & attrPath) /* Computing levels/displacements for variables. */ -void Expr::bindVars(EvalState & es, const std::shared_ptr & env) +void Expr::bindVars(Evaluator & es, const std::shared_ptr & env) { abort(); } -void ExprInt::bindVars(EvalState & es, const std::shared_ptr & env) +void ExprInt::bindVars(Evaluator & es, const std::shared_ptr & env) { if (es.debug) es.debug->exprEnvs.insert(std::make_pair(this, env)); } -void ExprFloat::bindVars(EvalState & es, const std::shared_ptr & env) +void ExprFloat::bindVars(Evaluator & es, const std::shared_ptr & env) { if (es.debug) es.debug->exprEnvs.insert(std::make_pair(this, env)); } -void ExprString::bindVars(EvalState & es, const std::shared_ptr & env) +void ExprString::bindVars(Evaluator & es, const std::shared_ptr & env) { if (es.debug) es.debug->exprEnvs.insert(std::make_pair(this, env)); } -void ExprPath::bindVars(EvalState & es, const std::shared_ptr & env) +void ExprPath::bindVars(Evaluator & es, const std::shared_ptr & env) { if (es.debug) es.debug->exprEnvs.insert(std::make_pair(this, env)); } -void ExprVar::bindVars(EvalState & es, const std::shared_ptr & env) +void ExprVar::bindVars(Evaluator & es, const std::shared_ptr & env) { if (es.debug) es.debug->exprEnvs.insert(std::make_pair(this, env)); @@ -373,13 +373,13 @@ void ExprVar::bindVars(EvalState & es, const std::shared_ptr & this->level = withLevel; } -void ExprInheritFrom::bindVars(EvalState & es, const std::shared_ptr & env) +void ExprInheritFrom::bindVars(Evaluator & es, const std::shared_ptr & env) { if (es.debug) es.debug->exprEnvs.insert(std::make_pair(this, env)); } -void ExprSelect::bindVars(EvalState & es, const std::shared_ptr & env) +void ExprSelect::bindVars(Evaluator & es, const std::shared_ptr & env) { if (es.debug) es.debug->exprEnvs.insert(std::make_pair(this, env)); @@ -391,7 +391,7 @@ void ExprSelect::bindVars(EvalState & es, const std::shared_ptr i.expr->bindVars(es, env); } -void ExprOpHasAttr::bindVars(EvalState & es, const std::shared_ptr & env) +void ExprOpHasAttr::bindVars(Evaluator & es, const std::shared_ptr & env) { if (es.debug) es.debug->exprEnvs.insert(std::make_pair(this, env)); @@ -403,7 +403,7 @@ void ExprOpHasAttr::bindVars(EvalState & es, const std::shared_ptr ExprAttrs::bindInheritSources( - EvalState & es, const std::shared_ptr & env) + Evaluator & es, const std::shared_ptr & env) { if (!inheritFromExprs) return nullptr; @@ -423,7 +423,7 @@ std::shared_ptr ExprAttrs::bindInheritSources( return inner; } -void ExprAttrs::bindVars(EvalState & es, const std::shared_ptr & env) +void ExprAttrs::bindVars(Evaluator & es, const std::shared_ptr & env) { if (es.debug) es.debug->exprEnvs.insert(std::make_pair(this, env)); @@ -462,7 +462,7 @@ void ExprAttrs::bindVars(EvalState & es, const std::shared_ptr } } -void ExprList::bindVars(EvalState & es, const std::shared_ptr & env) +void ExprList::bindVars(Evaluator & es, const std::shared_ptr & env) { if (es.debug) es.debug->exprEnvs.insert(std::make_pair(this, env)); @@ -471,7 +471,7 @@ void ExprList::bindVars(EvalState & es, const std::shared_ptr & i->bindVars(es, env); } -void ExprLambda::bindVars(EvalState & es, const std::shared_ptr & env) +void ExprLambda::bindVars(Evaluator & es, const std::shared_ptr & env) { if (es.debug) es.debug->exprEnvs.insert(std::make_pair(this, env)); @@ -498,7 +498,7 @@ void ExprLambda::bindVars(EvalState & es, const std::shared_ptr body->bindVars(es, newEnv); } -void ExprCall::bindVars(EvalState & es, const std::shared_ptr & env) +void ExprCall::bindVars(Evaluator & es, const std::shared_ptr & env) { if (es.debug) es.debug->exprEnvs.insert(std::make_pair(this, env)); @@ -508,7 +508,7 @@ void ExprCall::bindVars(EvalState & es, const std::shared_ptr & e->bindVars(es, env); } -void ExprLet::bindVars(EvalState & es, const std::shared_ptr & env) +void ExprLet::bindVars(Evaluator & es, const std::shared_ptr & env) { auto newEnv = [&] () -> std::shared_ptr { auto newEnv = std::make_shared(nullptr, env.get(), attrs->attrs.size()); @@ -531,7 +531,7 @@ void ExprLet::bindVars(EvalState & es, const std::shared_ptr & body->bindVars(es, newEnv); } -void ExprWith::bindVars(EvalState & es, const std::shared_ptr & env) +void ExprWith::bindVars(Evaluator & es, const std::shared_ptr & env) { if (es.debug) es.debug->exprEnvs.insert(std::make_pair(this, env)); @@ -557,7 +557,7 @@ void ExprWith::bindVars(EvalState & es, const std::shared_ptr & body->bindVars(es, newEnv); } -void ExprIf::bindVars(EvalState & es, const std::shared_ptr & env) +void ExprIf::bindVars(Evaluator & es, const std::shared_ptr & env) { if (es.debug) es.debug->exprEnvs.insert(std::make_pair(this, env)); @@ -567,7 +567,7 @@ void ExprIf::bindVars(EvalState & es, const std::shared_ptr & e else_->bindVars(es, env); } -void ExprAssert::bindVars(EvalState & es, const std::shared_ptr & env) +void ExprAssert::bindVars(Evaluator & es, const std::shared_ptr & env) { if (es.debug) es.debug->exprEnvs.insert(std::make_pair(this, env)); @@ -576,7 +576,7 @@ void ExprAssert::bindVars(EvalState & es, const std::shared_ptr body->bindVars(es, env); } -void ExprOpNot::bindVars(EvalState & es, const std::shared_ptr & env) +void ExprOpNot::bindVars(Evaluator & es, const std::shared_ptr & env) { if (es.debug) es.debug->exprEnvs.insert(std::make_pair(this, env)); @@ -584,7 +584,7 @@ void ExprOpNot::bindVars(EvalState & es, const std::shared_ptr e->bindVars(es, env); } -void ExprConcatStrings::bindVars(EvalState & es, const std::shared_ptr & env) +void ExprConcatStrings::bindVars(Evaluator & es, const std::shared_ptr & env) { if (es.debug) es.debug->exprEnvs.insert(std::make_pair(this, env)); @@ -593,7 +593,7 @@ void ExprConcatStrings::bindVars(EvalState & es, const std::shared_ptrbindVars(es, env); } -void ExprPos::bindVars(EvalState & es, const std::shared_ptr & env) +void ExprPos::bindVars(Evaluator & es, const std::shared_ptr & env) { if (es.debug) es.debug->exprEnvs.insert(std::make_pair(this, env)); diff --git a/lix/libexpr/nixexpr.hh b/lix/libexpr/nixexpr.hh index 292ed18ef..bf2a3e8dc 100644 --- a/lix/libexpr/nixexpr.hh +++ b/lix/libexpr/nixexpr.hh @@ -18,7 +18,7 @@ namespace nix { struct Env; struct Value; -class EvalState; +class Evaluator; struct ExprWith; struct StaticEnv; @@ -59,7 +59,7 @@ public: virtual ~Expr() { }; virtual void show(const SymbolTable & symbols, std::ostream & str) const; - virtual void bindVars(EvalState & es, const std::shared_ptr & env); + virtual void bindVars(Evaluator & es, const std::shared_ptr & env); virtual void eval(EvalState & state, Env & env, Value & v); virtual Value * maybeThunk(EvalState & state, Env & env); virtual void setName(Symbol name); @@ -69,7 +69,7 @@ public: #define COMMON_METHODS \ void show(const SymbolTable & symbols, std::ostream & str) const override; \ void eval(EvalState & state, Env & env, Value & v) override; \ - void bindVars(EvalState & es, const std::shared_ptr & env) override; + void bindVars(Evaluator & es, const std::shared_ptr & env) override; struct ExprInt : Expr { @@ -163,7 +163,7 @@ struct ExprInheritFrom : ExprVar } void show(SymbolTable const & symbols, std::ostream & str) const override; - void bindVars(EvalState & es, const std::shared_ptr & env) override; + void bindVars(Evaluator & es, const std::shared_ptr & env) override; }; struct ExprSelect : Expr @@ -249,7 +249,7 @@ struct ExprAttrs : Expr COMMON_METHODS std::shared_ptr bindInheritSources( - EvalState & es, const std::shared_ptr & env); + Evaluator & es, const std::shared_ptr & env); Env * buildInheritFromEnv(EvalState & state, Env & up); void showBindings(const SymbolTable & symbols, std::ostream & str) const; }; @@ -424,7 +424,7 @@ struct ExprOpNot : Expr { \ str << "("; e1->show(symbols, str); str << " " s " "; e2->show(symbols, str); str << ")"; \ } \ - void bindVars(EvalState & es, const std::shared_ptr & env) override \ + void bindVars(Evaluator & es, const std::shared_ptr & env) override \ { \ e1->bindVars(es, env); e2->bindVars(es, env); \ } \ @@ -464,7 +464,7 @@ struct ExprBlackHole : Expr { void show(const SymbolTable & symbols, std::ostream & str) const override {} void eval(EvalState & state, Env & env, Value & v) override; - void bindVars(EvalState & es, const std::shared_ptr & env) override {} + void bindVars(Evaluator & es, const std::shared_ptr & env) override {} }; extern ExprBlackHole eBlackHole; diff --git a/lix/libexpr/parser/parser.cc b/lix/libexpr/parser/parser.cc index 82862e999..85c2000e5 100644 --- a/lix/libexpr/parser/parser.cc +++ b/lix/libexpr/parser/parser.cc @@ -22,7 +22,7 @@ namespace nix { -Expr * EvalState::parse( +Expr * Evaluator::parse( char * text, size_t length, Pos::Origin origin, diff --git a/lix/libexpr/primops/fetchTree.cc b/lix/libexpr/primops/fetchTree.cc index f382144dd..c81d5ae91 100644 --- a/lix/libexpr/primops/fetchTree.cc +++ b/lix/libexpr/primops/fetchTree.cc @@ -15,7 +15,7 @@ namespace nix { void emitTreeAttrs( - EvalState & state, + Evaluator & state, const fetchers::Tree & tree, const fetchers::Input & input, Value & v, @@ -196,7 +196,7 @@ static void fetchTree( state.ctx.paths.allowPath(tree.storePath); - emitTreeAttrs(state, tree, input2, v, params.emptyRevFallback, false); + emitTreeAttrs(state.ctx, tree, input2, v, params.emptyRevFallback, false); } static void prim_fetchTree(EvalState & state, const PosIdx pos, Value * * args, Value & v) diff --git a/lix/nix/build.cc b/lix/nix/build.cc index bd7e1a84b..cdd6c3b70 100644 --- a/lix/nix/build.cc +++ b/lix/nix/build.cc @@ -114,7 +114,7 @@ struct CmdBuild : InstallablesCommand, MixDryRun, MixJSON, MixProfile void run(ref store, Installables && installables) override { - auto state = getEvalState(); + auto state = getEvaluator()->begin(); if (dryRun) { std::vector pathsToBuild; diff --git a/lix/nix/bundle.cc b/lix/nix/bundle.cc index aa6d51c09..bf2ba002e 100644 --- a/lix/nix/bundle.cc +++ b/lix/nix/bundle.cc @@ -73,8 +73,8 @@ struct CmdBundle : InstallableCommand void run(ref store, ref installable) override { - auto evaluator = getEvalState(); - auto evalState = evaluator; + auto evaluator = getEvaluator(); + auto evalState = evaluator->begin(); auto const installableValue = InstallableValue::require(installable); diff --git a/lix/nix/derivation-show.cc b/lix/nix/derivation-show.cc index fc290b7db..0053b8f0a 100644 --- a/lix/nix/derivation-show.cc +++ b/lix/nix/derivation-show.cc @@ -41,7 +41,7 @@ struct CmdShowDerivation : InstallablesCommand void run(ref store, Installables && installables) override { - auto drvPaths = Installable::toDerivations(*getEvalState(), store, installables, true); + auto drvPaths = Installable::toDerivations(*getEvaluator()->begin(), store, installables, true); if (recursive) { StorePathSet closure; diff --git a/lix/nix/develop.cc b/lix/nix/develop.cc index 439f33fc8..4e420ce9d 100644 --- a/lix/nix/develop.cc +++ b/lix/nix/develop.cc @@ -313,6 +313,7 @@ struct Common : InstallableCommand, MixProfile } std::string makeRcScript( + EvalState & state, ref store, const BuildEnvironment & buildEnvironment, const Path & tmpDir, @@ -364,9 +365,9 @@ struct Common : InstallableCommand, MixProfile /* Substitute redirects. */ for (auto & [installable_, dir_] : redirects) { auto dir = absPath(dir_); - auto installable = parseInstallable(*getEvalState(), store, installable_); + auto installable = parseInstallable(state, store, installable_); auto builtPaths = Installable::toStorePathSet( - *getEvalState(), getEvalStore(), store, Realise::Nothing, OperateOn::Output, {installable}); + state, getEvalStore(), store, Realise::Nothing, OperateOn::Output, {installable}); for (auto & path: builtPaths) { auto from = store->printStorePath(path); if (script.find(from) == std::string::npos) @@ -438,13 +439,13 @@ struct Common : InstallableCommand, MixProfile return res; } - StorePath getShellOutPath(ref store, ref installable) + StorePath getShellOutPath(EvalState & state, ref store, ref installable) { auto path = installable->getStorePath(); if (path && path->to_string().ends_with("-env")) return *path; else { - auto drvs = Installable::toDerivations(*getEvalState(), store, {installable}); + auto drvs = Installable::toDerivations(state, store, {installable}); if (drvs.size() != 1) throw Error("'%s' needs to evaluate to a single derivation, but it evaluated to %d derivations", @@ -457,9 +458,9 @@ struct Common : InstallableCommand, MixProfile } std::pair - getBuildEnvironment(ref store, ref installable) + getBuildEnvironment(EvalState & state, ref store, ref installable) { - auto shellOutPath = getShellOutPath(store, installable); + auto shellOutPath = getShellOutPath(state, store, installable); auto strPath = store->printStorePath(shellOutPath); @@ -547,16 +548,16 @@ struct CmdDevelop : Common, MixEnvironment void run(ref store, ref installable) override { - auto evaluator = getEvalState(); - auto state = evaluator; + auto evaluator = getEvaluator(); + auto state = evaluator->begin(); - auto [buildEnvironment, gcroot] = getBuildEnvironment(store, installable); + auto [buildEnvironment, gcroot] = getBuildEnvironment(*state, store, installable); auto [rcFileFd, rcFilePath] = createTempFile("nix-shell"); AutoDelete tmpDir(createTempDir("", "nix-develop"), true); - auto script = makeRcScript(store, buildEnvironment, (Path) tmpDir); + auto script = makeRcScript(*state, store, buildEnvironment, (Path) tmpDir); if (verbosity >= lvlDebug) script += "set -x\n"; @@ -611,7 +612,7 @@ struct CmdDevelop : Common, MixEnvironment auto bashInstallable = make_ref( this, - state, + evaluator, std::move(nixpkgs), "bashInteractive", ExtendedOutputsSpec::Default(), @@ -651,7 +652,7 @@ struct CmdDevelop : Common, MixEnvironment // chdir if installable is a flake of type git+file or path auto installableFlake = installable.dynamic_pointer_cast(); if (installableFlake) { - auto sourcePath = installableFlake->getLockedFlake(*getEvalState())->flake.resolvedRef.input.getSourcePath(); + auto sourcePath = installableFlake->getLockedFlake(*state)->flake.resolvedRef.input.getSourcePath(); if (sourcePath) { if (chdir(sourcePath->c_str()) == -1) { throw SysError("chdir to '%s' failed", *sourcePath); @@ -682,7 +683,8 @@ struct CmdPrintDevEnv : Common, MixJSON void run(ref store, ref installable) override { - auto buildEnvironment = getBuildEnvironment(store, installable).first; + auto state = getEvaluator()->begin(); + auto buildEnvironment = getBuildEnvironment(*state, store, installable).first; logger->pause(); @@ -690,7 +692,7 @@ struct CmdPrintDevEnv : Common, MixJSON logger->writeToStdout(buildEnvironment.toJSON()); } else { AutoDelete tmpDir(createTempDir("", "nix-dev-env"), true); - logger->writeToStdout(makeRcScript(store, buildEnvironment, tmpDir)); + logger->writeToStdout(makeRcScript(*state, store, buildEnvironment, tmpDir)); } } }; diff --git a/lix/nix/diff-closures.cc b/lix/nix/diff-closures.cc index b44ed1c8f..33f72b653 100644 --- a/lix/nix/diff-closures.cc +++ b/lix/nix/diff-closures.cc @@ -124,7 +124,7 @@ struct CmdDiffClosures : SourceExprCommand, MixOperateOnOptions void run(ref store) override { - auto state = getEvalState(); + auto state = getEvaluator()->begin(); auto before = parseInstallable(*state, store, _before); auto beforePath = Installable::toStorePath(*state, getEvalStore(), store, Realise::Outputs, operateOn, before); auto after = parseInstallable(*state, store, _after); diff --git a/lix/nix/edit.cc b/lix/nix/edit.cc index fabfca3d1..46b818327 100644 --- a/lix/nix/edit.cc +++ b/lix/nix/edit.cc @@ -27,8 +27,8 @@ struct CmdEdit : InstallableCommand void run(ref store, ref installable) override { - auto evaluator = getEvalState(); - auto state = evaluator; + auto evaluator = getEvaluator(); + auto state = evaluator->begin(); auto const installableValue = InstallableValue::require(installable); diff --git a/lix/nix/eval.cc b/lix/nix/eval.cc index 942e7f122..31d1a2ac2 100644 --- a/lix/nix/eval.cc +++ b/lix/nix/eval.cc @@ -61,8 +61,8 @@ struct CmdEval : MixJSON, InstallableCommand, MixReadOnlyOption auto const installableValue = InstallableValue::require(installable); - auto evaluator = getEvalState(); - auto state = evaluator; + auto evaluator = getEvaluator(); + auto state = evaluator->begin(); auto [v, pos] = installableValue->toValue(*state); NixStringContext context; diff --git a/lix/nix/flake.cc b/lix/nix/flake.cc index 3aa93ddaa..c0797e225 100644 --- a/lix/nix/flake.cc +++ b/lix/nix/flake.cc @@ -51,9 +51,9 @@ public: return parseFlakeRef(flakeUrl, absPath(".")); //FIXME } - LockedFlake lockFlake() + LockedFlake lockFlake(EvalState & state) { - return flake::lockFlake(*getEvalState(), getFlakeRef(), lockFlags); + return flake::lockFlake(state, getFlakeRef(), lockFlags); } std::vector getFlakeRefsForCompletion() override @@ -98,7 +98,7 @@ public: } }}, .completer = {[&](AddCompletions & completions, size_t, std::string_view prefix) { - completeFlakeInputPath(completions, getEvalState(), getFlakeRefsForCompletion(), prefix); + completeFlakeInputPath(completions, *getEvaluator()->begin(), getFlakeRefsForCompletion(), prefix); }} }); @@ -123,7 +123,7 @@ public: lockFlags.writeLockFile = true; lockFlags.applyNixConfig = true; - lockFlake(); + lockFlake(*getEvaluator()->begin()); } }; @@ -163,7 +163,7 @@ struct CmdFlakeLock : FlakeCommand lockFlags.writeLockFile = true; lockFlags.applyNixConfig = true; - lockFlake(); + lockFlake(*getEvaluator()->begin()); } }; @@ -208,7 +208,7 @@ struct CmdFlakeMetadata : FlakeCommand, MixJSON void run(nix::ref store) override { - auto lockedFlake = lockFlake(); + auto lockedFlake = lockFlake(*getEvaluator()->begin()); auto & flake = lockedFlake.flake; auto formatTime = [](time_t time) -> std::string { std::ostringstream os{}; @@ -358,11 +358,11 @@ struct CmdFlakeCheck : FlakeCommand evalSettings.enableImportFromDerivation.setDefault(false); } - auto evaluator = getEvalState(); - auto state = evaluator; + auto evaluator = getEvaluator(); + auto state = evaluator->begin(); lockFlags.applyNixConfig = true; - auto flake = lockFlake(); + auto flake = lockFlake(*state); auto localSystem = std::string(settings.thisSystem.get()); bool hasErrors = false; @@ -840,7 +840,8 @@ struct CmdFlakeInitCommon : virtual Args, EvalCommand .completer = {[&](AddCompletions & completions, size_t, std::string_view prefix) { completeFlakeRefWithFragment( completions, - getEvalState(), + *getEvaluator()->begin(), + getEvaluator(), lockFlags, defaultTemplateAttrPathsPrefixes, defaultTemplateAttrPaths, @@ -853,8 +854,8 @@ struct CmdFlakeInitCommon : virtual Args, EvalCommand { auto flakeDir = absPath(destDir); - auto evaluator = getEvalState(); - auto evalState = evaluator; + auto evaluator = getEvaluator(); + auto evalState = evaluator->begin(); auto [templateFlakeRef, templateName] = parseFlakeRefWithFragment(templateUrl, absPath(".")); @@ -1049,7 +1050,7 @@ struct CmdFlakeArchive : FlakeCommand, MixJSON, MixDryRun void run(nix::ref store) override { - auto flake = lockFlake(); + auto flake = lockFlake(*getEvaluator()->begin()); StorePathSet sources; @@ -1132,9 +1133,9 @@ struct CmdFlakeShow : FlakeCommand, MixJSON { evalSettings.enableImportFromDerivation.setDefault(false); - auto evaluator = getEvalState(); - auto state = evaluator; - auto flake = std::make_shared(lockFlake()); + auto evaluator = getEvaluator(); + auto state = evaluator->begin(); + auto flake = std::make_shared(lockFlake(*state)); auto localSystem = std::string(settings.thisSystem.get()); std::function store) override { - auto evalState = getEvalState(); + auto evaluator = getEvaluator(); auto evalStore = getEvalStore(); + auto state = evaluator->begin(); - auto installable_ = parseInstallable(*evalState, store, "."); + auto installable_ = parseInstallable(*state, store, "."); auto & installable = InstallableValue::require(*installable_); - auto app = installable.toApp(*evalState).resolve(*evalState, evalStore, store); + auto app = installable.toApp(*state).resolve(*state, evalStore, store); Strings programArgs{app.program}; diff --git a/lix/nix/log.cc b/lix/nix/log.cc index 1dd853d8e..5081da95d 100644 --- a/lix/nix/log.cc +++ b/lix/nix/log.cc @@ -30,7 +30,7 @@ struct CmdLog : InstallableCommand subs.push_front(store); - auto b = installable->toDerivedPath(*getEvalState()); + auto b = installable->toDerivedPath(*getEvaluator()->begin()); // For compat with CLI today, TODO revisit auto oneUp = std::visit(overloaded { diff --git a/lix/nix/main.cc b/lix/nix/main.cc index 5ec41c69e..9e1bf1376 100644 --- a/lix/nix/main.cc +++ b/lix/nix/main.cc @@ -254,8 +254,8 @@ static void showHelp(std::vector subcommand, NixArgs & toplevel) evalSettings.restrictEval.override(false); evalSettings.pureEval.override(false); - EvalState evaluator({}, openStore("dummy://")); - auto * state = &evaluator; + Evaluator evaluator({}, openStore("dummy://")); + auto state = evaluator.begin(); auto vGenerateManpage = evaluator.mem.allocValue(); state->eval(evaluator.parseExprFromString( @@ -411,7 +411,7 @@ void mainWrapped(int argc, char * * argv) | Xp::FetchClosure | Xp::DynamicDerivations); evalSettings.pureEval.override(false); - EvalState state({}, openStore("dummy://")); + Evaluator state({}, openStore("dummy://")); auto res = nlohmann::json::object(); res["builtins"] = ({ auto builtinsJson = nlohmann::json::object(); diff --git a/lix/nix/prefetch.cc b/lix/nix/prefetch.cc index 04fed2ba0..9457ad797 100644 --- a/lix/nix/prefetch.cc +++ b/lix/nix/prefetch.cc @@ -1,4 +1,5 @@ #include "lix/libcmd/command.hh" +#include "lix/libexpr/eval.hh" #include "lix/libmain/common-args.hh" #include "lix/libmain/loggers.hh" #include "lix/libmain/shared.hh" @@ -185,8 +186,8 @@ static int main_nix_prefetch_url(std::string programName, Strings argv) setLogFormat(LogFormat::bar); auto store = openStore(); - auto evaluator = std::make_unique(myArgs.searchPath, store); - auto & state = evaluator; + auto evaluator = std::make_unique(myArgs.searchPath, store); + auto state = evaluator->begin(); Bindings & autoArgs = *myArgs.getAutoArgs(*evaluator); diff --git a/lix/nix/profile.cc b/lix/nix/profile.cc index cd55a32bf..37e9c8a95 100644 --- a/lix/nix/profile.cc +++ b/lix/nix/profile.cc @@ -69,11 +69,12 @@ struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile void run(ref store, Installables && installables) override { - ProfileManifest manifest(*getEvalState(), *profile); + auto state = getEvaluator()->begin(); + ProfileManifest manifest(*state, *profile); auto builtPaths = builtPathsPerInstallable( Installable::build2( - *getEvalState(), getEvalStore(), store, Realise::Outputs, installables, bmNormal)); + *state, getEvalStore(), store, Realise::Outputs, installables, bmNormal)); for (auto & installable : installables) { ProfileElement element; @@ -240,7 +241,7 @@ struct CmdProfileRemove : virtual EvalCommand, MixDefaultProfile, MixProfileElem void run(ref store) override { - ProfileManifest oldManifest(*getEvalState(), *profile); + ProfileManifest oldManifest(*getEvaluator()->begin(), *profile); auto matchers = getMatchers(store); @@ -289,7 +290,8 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf void run(ref store) override { - ProfileManifest manifest(*getEvalState(), *profile); + auto state = getEvaluator()->begin(); + ProfileManifest manifest(*state, *profile); auto matchers = getMatchers(store); @@ -334,7 +336,7 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf auto installable = make_ref( this, - getEvalState(), + getEvaluator(), FlakeRef(element.source->originalRef), "", element.source->outputs, @@ -343,7 +345,7 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf lockFlags ); - auto derivedPaths = installable->toDerivedPaths(*getEvalState()); + auto derivedPaths = installable->toDerivedPaths(*state); if (derivedPaths.empty()) { continue; } @@ -393,7 +395,7 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf auto builtPaths = builtPathsPerInstallable( Installable::build2( - *getEvalState(), getEvalStore(), store, Realise::Outputs, installables, bmNormal)); + *state, getEvalStore(), store, Realise::Outputs, installables, bmNormal)); for (size_t i = 0; i < installables.size(); ++i) { auto & installable = installables.at(i); @@ -424,7 +426,7 @@ struct CmdProfileList : virtual EvalCommand, virtual StoreCommand, MixDefaultPro void run(ref store) override { - ProfileManifest manifest(*getEvalState(), *profile); + ProfileManifest manifest(*getEvaluator()->begin(), *profile); if (json) { std::cout << manifest.toJSON(*store).dump() << "\n"; @@ -509,7 +511,7 @@ struct CmdProfileHistory : virtual StoreCommand, EvalCommand, MixDefaultProfile bool first = true; for (auto & gen : gens) { - ProfileManifest manifest(*getEvalState(), gen.path); + ProfileManifest manifest(*getEvaluator()->begin(), gen.path); if (!first) logger->cout(""); first = false; diff --git a/lix/nix/repl.cc b/lix/nix/repl.cc index c6ba796ac..655824fdf 100644 --- a/lix/nix/repl.cc +++ b/lix/nix/repl.cc @@ -63,8 +63,8 @@ struct CmdRepl : RawInstallablesCommand void run(ref store, std::vector && rawInstallables) override { - auto evaluator = getEvalState(); - auto state = evaluator; + auto evaluator = getEvaluator(); + auto state = evaluator->begin(); auto getValues = [&]()->AbstractNixRepl::AnnotatedValues{ auto installables = parseInstallables(*state, store, rawInstallables); AbstractNixRepl::AnnotatedValues values; diff --git a/lix/nix/run.cc b/lix/nix/run.cc index 8df68c343..e4b8a0035 100644 --- a/lix/nix/run.cc +++ b/lix/nix/run.cc @@ -111,7 +111,7 @@ struct CmdShell : InstallablesCommand, MixEnvironment void run(ref store, Installables && installables) override { - auto outPaths = Installable::toStorePaths(*getEvalState(), getEvalStore(), store, Realise::Outputs, OperateOn::Output, installables); + auto outPaths = Installable::toStorePaths(*getEvaluator()->begin(), getEvalStore(), store, Realise::Outputs, OperateOn::Output, installables); auto accessor = store->getFSAccessor(); @@ -200,7 +200,7 @@ struct CmdRun : InstallableCommand void run(ref store, ref installable) override { - auto state = getEvalState(); + auto state = getEvaluator()->begin(); auto installableValue = InstallableValue::require(installable); diff --git a/lix/nix/search.cc b/lix/nix/search.cc index 275dfbbb3..58c8a3890 100644 --- a/lix/nix/search.cc +++ b/lix/nix/search.cc @@ -84,8 +84,8 @@ struct CmdSearch : InstallableCommand, MixJSON for (auto & re : excludeRes) excludeRegexes.emplace_back(re, std::regex::extended | std::regex::icase); - auto evaluator = getEvalState(); - auto state = evaluator; + auto evaluator = getEvaluator(); + auto state = evaluator->begin(); std::optional jsonOut; if (json) jsonOut = json::object(); diff --git a/lix/nix/store-copy-log.cc b/lix/nix/store-copy-log.cc index 617024071..26b3c2fa1 100644 --- a/lix/nix/store-copy-log.cc +++ b/lix/nix/store-copy-log.cc @@ -31,7 +31,7 @@ struct CmdCopyLog : virtual CopyCommand, virtual InstallablesCommand auto dstStore = getDstStore(); auto & dstLogStore = require(*dstStore); - for (auto & drvPath : Installable::toDerivations(*getEvalState(), getEvalStore(), installables, true)) { + for (auto & drvPath : Installable::toDerivations(*getEvaluator()->begin(), getEvalStore(), installables, true)) { if (auto log = srcLogStore.getBuildLog(drvPath)) dstLogStore.addBuildLog(drvPath, *log); else diff --git a/lix/nix/upgrade-nix.cc b/lix/nix/upgrade-nix.cc index 5a457aa15..a49b1f08d 100644 --- a/lix/nix/upgrade-nix.cc +++ b/lix/nix/upgrade-nix.cc @@ -218,9 +218,9 @@ struct CmdUpgradeNix : MixDryRun, EvalCommand // nb: nothing actually gets evaluated here. // The ProfileManifest constructor only evaluates anything for manifest.nix // profiles, which this is not. - auto evalState = this->getEvalState(); + auto evalState = this->getEvaluator(); - ProfileManifest manifest(*evalState, profileDir); + ProfileManifest manifest(*evalState->begin(), profileDir); // Find which profile element has Nix in it. // It should be impossible to *not* have Nix, since we grabbed this @@ -288,8 +288,8 @@ struct CmdUpgradeNix : MixDryRun, EvalCommand auto [res, content] = getFileTransfer()->download(storePathsUrl); auto data = content->drain(); - auto evaluator = std::make_unique(SearchPath{}, store); - auto & state = evaluator; + auto evaluator = std::make_unique(SearchPath{}, store); + auto state = evaluator->begin(); auto v = evaluator->mem.allocValue(); state->eval(evaluator->parseExprFromString(data, CanonPath("/no-such-path")), *v); Bindings & bindings(*evaluator->mem.allocBindings(0)); diff --git a/lix/nix/why-depends.cc b/lix/nix/why-depends.cc index 1a5246bb4..5ebc7cdd3 100644 --- a/lix/nix/why-depends.cc +++ b/lix/nix/why-depends.cc @@ -76,7 +76,7 @@ struct CmdWhyDepends : SourceExprCommand, MixOperateOnOptions void run(ref store) override { - auto state = getEvalState(); + auto state = getEvaluator()->begin(); auto package = parseInstallable(*state, store, _package); auto packagePath = Installable::toStorePath(*state, getEvalStore(), store, Realise::Outputs, operateOn, package); diff --git a/tests/unit/libcmd/args.cc b/tests/unit/libcmd/args.cc index dfb3ac3bd..4283dd663 100644 --- a/tests/unit/libcmd/args.cc +++ b/tests/unit/libcmd/args.cc @@ -31,7 +31,7 @@ TEST(Arguments, lookupFileArg) { searchPath.elements.push_back(SearchPath::Elem::parse(searchPathElem)); auto store = openStore("dummy://"); - auto state = std::make_shared(searchPath, store, store); + auto state = std::make_shared(searchPath, store, store); SourcePath const foundUnitData = lookupFileArg(*state, ""); EXPECT_EQ(foundUnitData.path, canonDataPath); diff --git a/tests/unit/libexpr-support/tests/libexpr.hh b/tests/unit/libexpr-support/tests/libexpr.hh index 41ccc2c72..aab674942 100644 --- a/tests/unit/libexpr-support/tests/libexpr.hh +++ b/tests/unit/libexpr-support/tests/libexpr.hh @@ -24,7 +24,8 @@ namespace nix { LibExprTest() : LibStoreTest() , evaluator({}, store) - , state(evaluator) + , statePtr(evaluator.begin()) + , state(*statePtr) { } Value eval(std::string input, bool forceValue = true, const FeatureSettings & fSettings = featureSettings) { @@ -40,7 +41,8 @@ namespace nix { return evaluator.symbols.create(value); } - EvalState evaluator; + Evaluator evaluator; + box_ptr statePtr; EvalState & state; };