diff options
author | John Ericson <John.Ericson@Obsidian.Systems> | 2022-07-14 16:15:37 -0400 |
---|---|---|
committer | John Ericson <John.Ericson@Obsidian.Systems> | 2022-07-14 16:15:37 -0400 |
commit | 6cafe308c946af7658514ad0e6970cc464554fbb (patch) | |
tree | 9354e960015fda89087f15808e6573b5ae39e025 /src/libexpr | |
parent | b585548dfee1bf12c63894c751f1449636e32565 (diff) | |
parent | ca4d5bee09df0393dd525b3cd5159a23d4683f2e (diff) |
Merge remote-tracking branch 'upstream/master' into indexed-store-path-outputs
Diffstat (limited to 'src/libexpr')
-rw-r--r-- | src/libexpr/eval-cache.cc | 4 | ||||
-rw-r--r-- | src/libexpr/eval.cc | 12 | ||||
-rw-r--r-- | src/libexpr/eval.hh | 14 | ||||
-rw-r--r-- | src/libexpr/flake/flake.cc | 33 | ||||
-rw-r--r-- | src/libexpr/nixexpr.hh | 24 | ||||
-rw-r--r-- | src/libexpr/parser.y | 6 | ||||
-rw-r--r-- | src/libexpr/primops.cc | 38 | ||||
-rw-r--r-- | src/libexpr/primops/fetchTree.cc | 4 | ||||
-rw-r--r-- | src/libexpr/tests/primops.cc | 24 |
9 files changed, 119 insertions, 40 deletions
diff --git a/src/libexpr/eval-cache.cc b/src/libexpr/eval-cache.cc index d77b25898..0d83b6cfe 100644 --- a/src/libexpr/eval-cache.cc +++ b/src/libexpr/eval-cache.cc @@ -282,7 +282,7 @@ struct AttrDb auto queryAttribute(state->queryAttribute.use()(key.first)(symbols[key.second])); if (!queryAttribute.next()) return {}; - auto rowId = (AttrType) queryAttribute.getInt(0); + auto rowId = (AttrId) queryAttribute.getInt(0); auto type = (AttrType) queryAttribute.getInt(1); switch (type) { @@ -486,7 +486,7 @@ std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(Symbol name, bool forceErro return nullptr; else if (std::get_if<failed_t>(&attr->second)) { if (forceErrors) - debug("reevaluating failed cached attribute '%s'"); + debug("reevaluating failed cached attribute '%s'", getAttrPathStr(name)); else throw CachedEvalError("cached failure of attribute '%s'", getAttrPathStr(name)); } else diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 40462afdf..f485e2fed 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -464,9 +464,10 @@ EvalState::EvalState( , emptyBindings(0) , store(store) , buildStore(buildStore ? buildStore : store) - , debugRepl(0) + , debugRepl(nullptr) , debugStop(false) , debugQuit(false) + , trylevel(0) , regexCache(makeRegexCache()) #if HAVE_BOEHMGC , valueAllocCache(std::allocate_shared<void *>(traceable_allocator<void *>(), nullptr)) @@ -832,7 +833,14 @@ void EvalState::runDebugRepl(const Error * error, const Env & env, const Expr & : nullptr; if (error) - printError("%s\n\n" ANSI_BOLD "Starting REPL to allow you to inspect the current state of the evaluator.\n" ANSI_NORMAL, error->what()); + { + printError("%s\n\n", error->what()); + + if (trylevel > 0 && error->info().level != lvlInfo) + printError("This exception occurred in a 'tryEval' call. Use " ANSI_GREEN "--ignore-try" ANSI_NORMAL " to skip these.\n"); + + printError(ANSI_BOLD "Starting REPL to allow you to inspect the current state of the evaluator.\n" ANSI_NORMAL); + } auto se = getStaticEnv(expr); if (se) { diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 7b8732169..f07f15d43 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -130,6 +130,7 @@ public: void (* debugRepl)(ref<EvalState> es, const ValMap & extraEnv); bool debugStop; bool debugQuit; + int trylevel; std::list<DebugTrace> debugTraces; std::map<const Expr*, const std::shared_ptr<const StaticEnv>> exprEnvs; const std::shared_ptr<const StaticEnv> getStaticEnv(const Expr & expr) const @@ -150,7 +151,7 @@ public: if (debugRepl) runDebugRepl(&error, env, expr); - throw error; + throw std::move(error); } template<class E> @@ -165,7 +166,7 @@ public: runDebugRepl(&e, last.env, last.expr); } - throw e; + throw std::move(e); } @@ -646,6 +647,15 @@ struct EvalSettings : Config Setting<bool> useEvalCache{this, true, "eval-cache", "Whether to use the flake evaluation cache."}; + + Setting<bool> ignoreExceptionsDuringTry{this, false, "ignore-try", + R"( + If set to true, ignore exceptions inside 'tryEval' calls when evaluating nix expressions in + debug mode (using the --debugger flag). By default the debugger will pause on all exceptions. + )"}; + + Setting<bool> traceVerbose{this, false, "trace-verbose", + "Whether `builtins.traceVerbose` should trace its first argument when evaluated."}; }; extern EvalSettings evalSettings; diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc index 35c841897..cc9be1336 100644 --- a/src/libexpr/flake/flake.cc +++ b/src/libexpr/flake/flake.cc @@ -384,6 +384,18 @@ LockedFlake lockFlake( } } + /* Check whether this input has overrides for a + non-existent input. */ + for (auto [inputPath, inputOverride] : overrides) { + auto inputPath2(inputPath); + auto follow = inputPath2.back(); + inputPath2.pop_back(); + if (inputPath2 == inputPathPrefix && !flakeInputs.count(follow)) + warn( + "input '%s' has an override for a non-existent input '%s'", + printInputPath(inputPathPrefix), follow); + } + /* Go over the flake inputs, resolve/fetch them if necessary (i.e. if they're new or the flakeref changed from what's in the lock file). */ @@ -513,6 +525,15 @@ LockedFlake lockFlake( if (!lockFlags.allowMutable && !input.ref->input.isLocked()) throw Error("cannot update flake input '%s' in pure mode", inputPathS); + /* Note: in case of an --override-input, we use + the *original* ref (input2.ref) for the + "original" field, rather than the + override. This ensures that the override isn't + nuked the next time we update the lock + file. That is, overrides are sticky unless you + use --no-write-lock-file. */ + auto ref = input2.ref ? *input2.ref : *input.ref; + if (input.isFlake) { Path localPath = parentPath; FlakeRef localRef = *input.ref; @@ -524,15 +545,7 @@ LockedFlake lockFlake( auto inputFlake = getFlake(state, localRef, useRegistries, flakeCache, inputPath); - /* Note: in case of an --override-input, we use - the *original* ref (input2.ref) for the - "original" field, rather than the - override. This ensures that the override isn't - nuked the next time we update the lock - file. That is, overrides are sticky unless you - use --no-write-lock-file. */ - auto childNode = std::make_shared<LockedNode>( - inputFlake.lockedRef, input2.ref ? *input2.ref : *input.ref); + auto childNode = std::make_shared<LockedNode>(inputFlake.lockedRef, ref); node->inputs.insert_or_assign(id, childNode); @@ -560,7 +573,7 @@ LockedFlake lockFlake( auto [sourceInfo, resolvedRef, lockedRef] = fetchOrSubstituteTree( state, *input.ref, useRegistries, flakeCache); node->inputs.insert_or_assign(id, - std::make_shared<LockedNode>(lockedRef, *input.ref, false)); + std::make_shared<LockedNode>(lockedRef, ref, false)); } } diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index 8813c61a9..5eb022770 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -150,16 +150,16 @@ struct Expr }; #define COMMON_METHODS \ - void show(const SymbolTable & symbols, std::ostream & str) const; \ - void eval(EvalState & state, Env & env, Value & v); \ - void bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env); + 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<const StaticEnv> & env) override; struct ExprInt : Expr { NixInt n; Value v; ExprInt(NixInt n) : n(n) { v.mkInt(n); }; - Value * maybeThunk(EvalState & state, Env & env); + Value * maybeThunk(EvalState & state, Env & env) override; COMMON_METHODS }; @@ -168,7 +168,7 @@ struct ExprFloat : Expr NixFloat nf; Value v; ExprFloat(NixFloat nf) : nf(nf) { v.mkFloat(nf); }; - Value * maybeThunk(EvalState & state, Env & env); + Value * maybeThunk(EvalState & state, Env & env) override; COMMON_METHODS }; @@ -177,7 +177,7 @@ struct ExprString : Expr std::string s; Value v; ExprString(std::string s) : s(std::move(s)) { v.mkString(this->s.data()); }; - Value * maybeThunk(EvalState & state, Env & env); + Value * maybeThunk(EvalState & state, Env & env) override; COMMON_METHODS }; @@ -186,7 +186,7 @@ struct ExprPath : Expr std::string s; Value v; ExprPath(std::string s) : s(std::move(s)) { v.mkPath(this->s.c_str()); }; - Value * maybeThunk(EvalState & state, Env & env); + Value * maybeThunk(EvalState & state, Env & env) override; COMMON_METHODS }; @@ -213,7 +213,7 @@ struct ExprVar : Expr ExprVar(Symbol name) : name(name) { }; ExprVar(const PosIdx & pos, Symbol name) : pos(pos), name(name) { }; - Value * maybeThunk(EvalState & state, Env & env); + Value * maybeThunk(EvalState & state, Env & env) override; PosIdx getPos() const override { return pos; } COMMON_METHODS }; @@ -326,7 +326,7 @@ struct ExprLambda : Expr : pos(pos), formals(formals), body(body) { } - void setName(Symbol name); + void setName(Symbol name) override; std::string showNamePos(const EvalState & state) const; inline bool hasFormals() const { return formals != nullptr; } PosIdx getPos() const override { return pos; } @@ -395,15 +395,15 @@ struct ExprOpNot : Expr Expr * e1, * e2; \ name(Expr * e1, Expr * e2) : e1(e1), e2(e2) { }; \ name(const PosIdx & pos, Expr * e1, Expr * e2) : pos(pos), e1(e1), e2(e2) { }; \ - void show(const SymbolTable & symbols, std::ostream & str) const \ + void show(const SymbolTable & symbols, std::ostream & str) const override \ { \ str << "("; e1->show(symbols, str); str << " " s " "; e2->show(symbols, str); str << ")"; \ } \ - void bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env) \ + void bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env) override \ { \ e1->bindVars(es, env); e2->bindVars(es, env); \ } \ - void eval(EvalState & state, Env & env, Value & v); \ + void eval(EvalState & state, Env & env, Value & v) override; \ PosIdx getPos() const override { return pos; } \ }; diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 8cbc2da4d..7c9b5a2db 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -520,6 +520,12 @@ path_start $$ = new ExprPath(path); } | HPATH { + if (evalSettings.pureEval) { + throw Error( + "the path '%s' can not be resolved in pure mode", + std::string_view($1.p, $1.l) + ); + } Path path(getHome() + std::string($1.p + 1, $1.l - 1)); $$ = new ExprPath(path); } diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index eea274301..bc253d0a3 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -851,6 +851,18 @@ static RegisterPrimOp primop_floor({ static void prim_tryEval(EvalState & state, const PosIdx pos, Value * * args, Value & v) { auto attrs = state.buildBindings(2); + + /* increment state.trylevel, and decrement it when this function returns. */ + MaintainCount trylevel(state.trylevel); + + void (* savedDebugRepl)(ref<EvalState> es, const ValMap & extraEnv) = nullptr; + if (state.debugRepl && evalSettings.ignoreExceptionsDuringTry) + { + /* to prevent starting the repl from exceptions withing a tryEval, null it. */ + savedDebugRepl = state.debugRepl; + state.debugRepl = nullptr; + } + try { state.forceValue(*args[0], pos); attrs.insert(state.sValue, args[0]); @@ -859,6 +871,11 @@ static void prim_tryEval(EvalState & state, const PosIdx pos, Value * * args, Va attrs.alloc(state.sValue).mkBool(false); attrs.alloc("success").mkBool(false); } + + // restore the debugRepl pointer if we saved it earlier. + if (savedDebugRepl) + state.debugRepl = savedDebugRepl; + v.mkAttrs(attrs); } @@ -970,6 +987,15 @@ static RegisterPrimOp primop_trace({ }); +/* Takes two arguments and evaluates to the second one. Used as the + * builtins.traceVerbose implementation when --trace-verbose is not enabled + */ +static void prim_second(EvalState & state, const PosIdx pos, Value * * args, Value & v) +{ + state.forceValue(*args[1], pos); + v = *args[1]; +} + /************************************************************* * Derivations *************************************************************/ @@ -3926,6 +3952,18 @@ void EvalState::createBaseEnv() addPrimOp("__exec", 1, prim_exec); } + addPrimOp({ + .fun = evalSettings.traceVerbose ? prim_trace : prim_second, + .arity = 2, + .name = "__traceVerbose", + .args = { "e1", "e2" }, + .doc = R"( + Evaluate *e1* and print its abstract syntax representation on standard + error if `--trace-verbose` is enabled. Then return *e2*. This function + is useful for debugging. + )", + }); + /* Add a value containing the current Nix expression search path. */ mkList(v, searchPath.size()); int n = 0; diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index e5eeea520..84e7f5c02 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -364,6 +364,10 @@ static RegisterPrimOp primop_fetchGit({ A Boolean parameter that specifies whether submodules should be checked out. Defaults to `false`. + - shallow\ + A Boolean parameter that specifies whether fetching a shallow clone + is allowed. Defaults to `false`. + - allRefs\ Whether to fetch all refs of the repository. With this argument being true, it's possible to load a `rev` from *any* `ref` (by default only diff --git a/src/libexpr/tests/primops.cc b/src/libexpr/tests/primops.cc index f65b6593d..16cf66d2c 100644 --- a/src/libexpr/tests/primops.cc +++ b/src/libexpr/tests/primops.cc @@ -540,22 +540,22 @@ namespace nix { ASSERT_THAT(v, IsStringEq(output)); } -#define CASE(input, output) (std::make_tuple(std::string_view("builtins.toString " #input), std::string_view(output))) +#define CASE(input, output) (std::make_tuple(std::string_view("builtins.toString " input), std::string_view(output))) INSTANTIATE_TEST_SUITE_P( toString, ToStringPrimOpTest, testing::Values( - CASE("foo", "foo"), - CASE(1, "1"), - CASE([1 2 3], "1 2 3"), - CASE(.123, "0.123000"), - CASE(true, "1"), - CASE(false, ""), - CASE(null, ""), - CASE({ v = "bar"; __toString = self: self.v; }, "bar"), - CASE({ v = "bar"; __toString = self: self.v; outPath = "foo"; }, "bar"), - CASE({ outPath = "foo"; }, "foo"), - CASE(./test, "/test") + CASE(R"("foo")", "foo"), + CASE(R"(1)", "1"), + CASE(R"([1 2 3])", "1 2 3"), + CASE(R"(.123)", "0.123000"), + CASE(R"(true)", "1"), + CASE(R"(false)", ""), + CASE(R"(null)", ""), + CASE(R"({ v = "bar"; __toString = self: self.v; })", "bar"), + CASE(R"({ v = "bar"; __toString = self: self.v; outPath = "foo"; })", "bar"), + CASE(R"({ outPath = "foo"; })", "foo"), + CASE(R"(./test)", "/test") ) ); #undef CASE |