diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index 08497854c..e184de9a7 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -429,7 +429,7 @@ Value * InstallableFlake::getFlakeOutputs(EvalState & state, const flake::Locked callFlake(state, lockedFlake, *vFlake); auto vRes = state.allocValue(); - auto gotField = state.getAttrField(*vFlake, {state.symbols.create("outputs")}, noPos, *vRes); + auto gotField = state.lazyGetAttrField(*vFlake, {state.symbols.create("outputs")}, noPos, *vRes); assert(gotField); return vRes; } diff --git a/src/libexpr/eval-inline.hh b/src/libexpr/eval-inline.hh index 4d8d07397..9b3b1e427 100644 --- a/src/libexpr/eval-inline.hh +++ b/src/libexpr/eval-inline.hh @@ -47,8 +47,10 @@ void EvalState::forceValue(Value & v, const Pos & pos) else if (v.isApp()) callFunction(*v.app.left, *v.app.right, v, noPos); else if (v.isCachedThunk()) { + auto evalCache = v.getEvalCache(); v.mkThunk(v.cachedThunk.thunk->env, v.cachedThunk.thunk->expr); forceValue(v, pos); + v.setEvalCache(evalCache); } else if (v.isBlackhole()) throwEvalError(pos, "infinite recursion encountered"); diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 9415ab46c..2643ad397 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -1189,28 +1189,13 @@ void EvalState::updateCacheStats(ValueCache::CacheResult cacheResult) }; } -struct ExprCastedVar : Expr -{ - Value * v; - ExprCastedVar(Value * v) : v(v) {}; - void show(std::ostream & str) const override { - std::set active; - printValue(str, active, *v); - } - - void bindVars(const StaticEnv & env) override {} - void eval(EvalState & state, Env & env, Value & v) override { - v = std::move(*this->v); - } - Value * maybeThunk(EvalState & state, Env & env) override { - return v; - } -}; - bool EvalState::lazyGetAttrField(Value & attrs, const std::vector & selector, const Pos & pos, Value & dest) { - forceValue(attrs, pos); auto eval_cache = attrs.getEvalCache(); + if (eval_cache.isEmpty()) { + forceValue(attrs, pos); + eval_cache = attrs.getEvalCache(); + } auto [ cacheResult, resultingCursor ] = eval_cache.getValue(*this, selector, dest); updateCacheStats(cacheResult); switch (cacheResult.returnCode) { @@ -1242,8 +1227,11 @@ bool EvalState::getAttrField(Value & attrs, const std::vector & selector { Pos * pos2 = 0; - forceValue(attrs, pos); auto eval_cache = attrs.getEvalCache(); + if (eval_cache.isEmpty()) { + forceValue(attrs, pos); + eval_cache = attrs.getEvalCache(); + } auto [ cacheResult, resultingCursor ] = eval_cache.getValue(*this, selector, dest); updateCacheStats(cacheResult); switch (cacheResult.returnCode) { @@ -1260,6 +1248,7 @@ bool EvalState::getAttrField(Value & attrs, const std::vector & selector ; } + forceValue(attrs, pos); Value * vAttrs = &attrs; try { diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc index 11112b07c..2445d1cc9 100644 --- a/src/libexpr/flake/flake.cc +++ b/src/libexpr/flake/flake.cc @@ -596,36 +596,44 @@ void callFlake(EvalState & state, const LockedFlake & lockedFlake, Value & vRes) { - auto vLocks = state.allocValue(); + auto lockExpr = new ExprString( + state.symbols.create(lockedFlake.lockFile.to_string()) + ); + auto subdirExpr = new ExprString( + state.symbols.create(lockedFlake.flake.lockedRef.subdir) + ); + auto vRootSrc = state.allocValue(); - auto vRootSubdir = state.allocValue(); - auto vTmp1 = state.allocValue(); - auto vTmp2 = state.allocValue(); - - mkString(*vLocks, lockedFlake.lockFile.to_string()); - emitTreeAttrs(state, *lockedFlake.flake.sourceInfo, lockedFlake.flake.lockedRef.input, *vRootSrc); - mkString(*vRootSubdir, lockedFlake.flake.lockedRef.subdir); - - static RootValue vCallFlake = nullptr; - - if (!vCallFlake) { - vCallFlake = allocRootValue(state.allocValue()); - state.eval(state.parseExprFromString( + static Expr * callFlakeExpr = nullptr; + if (!callFlakeExpr) { + callFlakeExpr = state.parseExprFromString( #include "call-flake.nix.gen.hh" - , "/"), **vCallFlake); + , "/"); } - state.callFunction(**vCallFlake, *vLocks, *vTmp1, noPos); - state.callFunction(*vTmp1, *vRootSrc, *vTmp2, noPos); - state.callFunction(*vTmp2, *vRootSubdir, vRes, noPos); - state.forceValue(vRes); + auto resExpr = new ExprApp( + new ExprApp( + new ExprApp( + callFlakeExpr, + lockExpr + ), + new ExprCastedVar(vRootSrc) + ), + subdirExpr + ); + auto thunk = (Thunk*)allocBytes(sizeof(Thunk)); + thunk->expr = resExpr; + thunk->env = &state.baseEnv; auto fingerprint = lockedFlake.getFingerprint(); auto treeCache = state.openTreeCache(fingerprint); auto cacheRoot = treeCache ? treeCache->getRoot() : nullptr; - auto evalCache = ValueCache(cacheRoot); - vRes.setEvalCache(evalCache); + auto evalCache = new ValueCache(cacheRoot); + vRes.mkCachedThunk( + thunk, + evalCache + ); } static void prim_getFlake(EvalState & state, const Pos & pos, Value * * args, Value & v) diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index 492b819e7..8d51fdce9 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -440,6 +440,18 @@ string ExprLambda::showNamePos() const return (format("%1% at %2%") % (name.set() ? "'" + (string) name + "'" : "anonymous function") % pos).str(); } +void ExprCastedVar::show(std::ostream & str) const { + std::set active; + printValue(str, active, *v); +} + +void ExprCastedVar::bindVars(const StaticEnv & env) {} +void ExprCastedVar::eval(EvalState & state, Env & env, Value & v) { + v = std::move(*this->v); +} +Value * ExprCastedVar::maybeThunk(EvalState & state, Env & env) { + return v; +} /* Symbol table. */ diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index da860e141..bee16c48f 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -339,6 +339,14 @@ struct ExprPos : Expr COMMON_METHODS }; +struct ExprCastedVar : Expr +{ + Value * v; + ExprCastedVar(Value * v) : v(v) {}; + Value * maybeThunk(EvalState & state, Env & env); + COMMON_METHODS +}; + /* Static environments are used to map variable names onto (level, displacement) pairs used to obtain the value of the variable at