diff --git a/lix/libexpr/eval.cc b/lix/libexpr/eval.cc index 0e0cc5301..250573ede 100644 --- a/lix/libexpr/eval.cc +++ b/lix/libexpr/eval.cc @@ -1039,40 +1039,34 @@ void EvalState::eval(Expr & e, Value & v) e.eval(*this, ctx.builtins.env, v); } +#define checkType(typeName, stringName) \ + if (v.type() != (typeName)) \ + ctx.errors.make( \ + "expected a %1% but found %2%: %3%", \ + Uncolored(stringName), \ + showType(v), \ + ValuePrinter(*this, v, errorPrintOptions) \ + ).atPos(e.getPos()).withFrame(env, e).debugThrow(); -inline bool EvalState::evalBool(Env & env, Expr & e, const PosIdx pos, std::string_view errorCtx) +inline bool EvalState::evalBool(Env & env, Expr & e) { - try { - Value v; - e.eval(*this, env, v); - if (v.type() != nBool) - ctx.errors.make( - "expected a Boolean but found %1%: %2%", - showType(v), - ValuePrinter(*this, v, errorPrintOptions) - ).atPos(pos).withFrame(env, e).debugThrow(); - return v.boolean; - } catch (Error & e) { - e.addTrace(ctx.positions[pos], errorCtx); - throw; - } + Value v; + e.eval(*this, env, v); + checkType(nBool, "Boolean"); + return v.boolean; } -inline void EvalState::evalAttrs(Env & env, Expr & e, Value & v, const PosIdx pos, std::string_view errorCtx) +inline void EvalState::evalAttrs(Env & env, Expr & e, Value & v) { - try { - e.eval(*this, env, v); - if (v.type() != nAttrs) - ctx.errors.make( - "expected a set but found %1%: %2%", - showType(v), - ValuePrinter(*this, v, errorPrintOptions) - ).withFrame(env, e).debugThrow(); - } catch (Error & e) { - e.addTrace(ctx.positions[pos], errorCtx); - throw; - } + e.eval(*this, env, v); + checkType(nAttrs, "set"); +} + +inline void EvalState::evalList(Env & env, Expr & e, Value & v) +{ + e.eval(*this, env, v); + checkType(nList, "list"); } @@ -1248,7 +1242,19 @@ Value * ExprList::maybeThunk(EvalState & state, Env & env) void ExprVar::eval(EvalState & state, Env & env, Value & v) { Value * v2 = state.lookupVar(&env, *this, false); - state.forceValue(*v2, pos); + try { + state.forceValue(*v2, pos); + } catch (Error & e) { + /* `name` can be invalid if we are an ExprInheritFrom */ + if (name) { + e.addTrace( + state.ctx.positions[getPos()], + "while evaluating %s", + state.ctx.symbols[name] + ); + } + throw; + } v = *v2; } @@ -1848,14 +1854,13 @@ void ExprWith::eval(EvalState & state, Env & env, Value & v) void ExprIf::eval(EvalState & state, Env & env, Value & v) { - // We cheat in the parser, and pass the position of the condition as the position of the if itself. - (state.evalBool(env, *cond, pos, "while evaluating a branch condition") ? *then : *else_).eval(state, env, v); + (state.evalBool(env, *cond) ? *then : *else_).eval(state, env, v); } void ExprAssert::eval(EvalState & state, Env & env, Value & v) { - if (!state.evalBool(env, *cond, pos, "in the condition of the assert statement")) { + if (!state.evalBool(env, *cond)) { state.ctx.errors.make("assertion failed") .atPos(pos) .withFrame(env, *this) @@ -1867,7 +1872,7 @@ void ExprAssert::eval(EvalState & state, Env & env, Value & v) void ExprOpNot::eval(EvalState & state, Env & env, Value & v) { - v.mkBool(!state.evalBool(env, *e, getPos(), "in the argument of the not operator")); // XXX: FIXME: ! + v.mkBool(!state.evalBool(env, *e)); } @@ -1889,27 +1894,27 @@ void ExprOpNEq::eval(EvalState & state, Env & env, Value & v) void ExprOpAnd::eval(EvalState & state, Env & env, Value & v) { - v.mkBool(state.evalBool(env, *e1, pos, "in the left operand of the AND (&&) operator") && state.evalBool(env, *e2, pos, "in the right operand of the AND (&&) operator")); + v.mkBool(state.evalBool(env, *e1) && state.evalBool(env, *e2)); } void ExprOpOr::eval(EvalState & state, Env & env, Value & v) { - v.mkBool(state.evalBool(env, *e1, pos, "in the left operand of the OR (||) operator") || state.evalBool(env, *e2, pos, "in the right operand of the OR (||) operator")); + v.mkBool(state.evalBool(env, *e1) || state.evalBool(env, *e2)); } void ExprOpImpl::eval(EvalState & state, Env & env, Value & v) { - v.mkBool(!state.evalBool(env, *e1, pos, "in the left operand of the IMPL (->) operator") || state.evalBool(env, *e2, pos, "in the right operand of the IMPL (->) operator")); + v.mkBool(!state.evalBool(env, *e1) || state.evalBool(env, *e2)); } void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v) { Value v1, v2; - state.evalAttrs(env, *e1, v1, pos, "in the left operand of the update (//) operator"); - state.evalAttrs(env, *e2, v2, pos, "in the right operand of the update (//) operator"); + state.evalAttrs(env, *e1, v1); + state.evalAttrs(env, *e2, v2); state.ctx.stats.nrOpUpdates++; @@ -1945,10 +1950,25 @@ void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v) void ExprOpConcatLists::eval(EvalState & state, Env & env, Value & v) { - Value v1; e1->eval(state, env, v1); - Value v2; e2->eval(state, env, v2); - Value * lists[2] = { &v1, &v2 }; - state.concatLists(v, 2, lists, pos, "while evaluating one of the elements to concatenate"); + state.ctx.stats.nrListConcats++; + + /* We don't call into `concatLists` as that loses the position information of the expressions. */ + + Value v1; state.evalList(env, *e1, v1); + Value v2; state.evalList(env, *e2, v2); + + size_t l1 = v1.listSize(), l2 = v2.listSize(), len = l1 + l2; + + if (l1 == 0) + v = v2; + else if (l2 == 0) + v = v1; + else { + v = state.ctx.mem.newList(len); + auto out = v.listElems(); + std::copy(v1.listElems(), v1.listElems() + l1, out); + std::copy(v2.listElems(), v2.listElems() + l2, out + l1); + } } diff --git a/lix/libexpr/eval.hh b/lix/libexpr/eval.hh index 99bd20bc2..6b96d46f8 100644 --- a/lix/libexpr/eval.hh +++ b/lix/libexpr/eval.hh @@ -717,8 +717,8 @@ public: * type. */ inline bool evalBool(Env & env, Expr & e); - inline bool evalBool(Env & env, Expr & e, const PosIdx pos, std::string_view errorCtx); - inline void evalAttrs(Env & env, Expr & e, Value & v, const PosIdx pos, std::string_view errorCtx); + inline void evalAttrs(Env & env, Expr & e, Value & v); + inline void evalList(Env & env, Expr & e, Value & v); /** * If `v` is a thunk, enter it and overwrite `v` with the result diff --git a/tests/functional/lang/eval-fail-blackhole.err.exp b/tests/functional/lang/eval-fail-blackhole.err.exp index 91fd293f3..1532258bf 100644 --- a/tests/functional/lang/eval-fail-blackhole.err.exp +++ b/tests/functional/lang/eval-fail-blackhole.err.exp @@ -1,4 +1,19 @@ -error: infinite recursion encountered +error: + … while evaluating x + at /pwd/lang/eval-fail-blackhole.nix:5:1: + 4| in + 5| x + | ^ + 6| + + … while evaluating y + at /pwd/lang/eval-fail-blackhole.nix:2:7: + 1| let + 2| x = y; + | ^ + 3| y = x; + + error: infinite recursion encountered at /pwd/lang/eval-fail-blackhole.nix:2:7: 1| let 2| x = y; diff --git a/tests/functional/lang/eval-fail-list.err.exp b/tests/functional/lang/eval-fail-list.err.exp index d492f8bd2..518b8095f 100644 --- a/tests/functional/lang/eval-fail-list.err.exp +++ b/tests/functional/lang/eval-fail-list.err.exp @@ -1,8 +1,5 @@ -error: - … while evaluating one of the elements to concatenate - at /pwd/lang/eval-fail-list.nix:1:2: +error: expected a list but found an integer: 8 + at /pwd/lang/eval-fail-list.nix:1:1: 1| 8++1 - | ^ + | ^ 2| - - error: expected a list but found an integer: 8 diff --git a/tests/functional/lang/eval-fail-not-throws.err.exp b/tests/functional/lang/eval-fail-not-throws.err.exp index adee0a493..677ed188d 100644 --- a/tests/functional/lang/eval-fail-not-throws.err.exp +++ b/tests/functional/lang/eval-fail-not-throws.err.exp @@ -1,10 +1,4 @@ error: - … in the argument of the not operator - at /pwd/lang/eval-fail-not-throws.nix:1:1: - 1| ! (throw "uh oh!") - | ^ - 2| - … caused by explicit throw at /pwd/lang/eval-fail-not-throws.nix:1:4: 1| ! (throw "uh oh!") diff --git a/tests/functional/lang/eval-fail-print-limit-list.err.exp b/tests/functional/lang/eval-fail-print-limit-list.err.exp index 2f347d15a..7a53c128b 100644 --- a/tests/functional/lang/eval-fail-print-limit-list.err.exp +++ b/tests/functional/lang/eval-fail-print-limit-list.err.exp @@ -1,12 +1,6 @@ -error: - … in the condition of the assert statement - at /pwd/lang/eval-fail-print-limit-list.nix:1:1: +error: expected a Boolean but found a list: [ 1 [ 2 3 4 5 6 7 8 9 [ 1 «2 items elided» ] «2 items elided» ] «1 item elided» ] + at /pwd/lang/eval-fail-print-limit-list.nix:2:3: 1| assert ( - | ^ - 2| let x = [ 1 [ 2 3 4 5 6 7 8 9 x 10 11 ] 12 ]; - - error: expected a Boolean but found a list: [ 1 [ 2 3 4 5 6 7 8 9 [ 1 «2 items elided» ] «2 items elided» ] «1 item elided» ] - at /pwd/lang/eval-fail-print-limit-list.nix:1:1: - 1| assert ( - | ^ 2| let x = [ 1 [ 2 3 4 5 6 7 8 9 x 10 11 ] 12 ]; + | ^ + 3| in builtins.deepSeq x x diff --git a/tests/functional/lang/eval-fail-print-limit-set.err.exp b/tests/functional/lang/eval-fail-print-limit-set.err.exp index c5086fb76..df9f6f79e 100644 --- a/tests/functional/lang/eval-fail-print-limit-set.err.exp +++ b/tests/functional/lang/eval-fail-print-limit-set.err.exp @@ -1,12 +1,6 @@ -error: - … in the condition of the assert statement - at /pwd/lang/eval-fail-print-limit-set.nix:1:1: +error: expected a Boolean but found a set: { a = { a = { a = { a = { a = { a = { a = { a = { a = { a = { ... }; b = 2; }; }; }; }; }; }; }; }; «1 attribute elided» }; «1 attribute elided» } + at /pwd/lang/eval-fail-print-limit-set.nix:2:3: 1| assert ( - | ^ - 2| let x = { a.a.a.a.a.a.a.a.a = { a.a = 1; b = 2; }; a.b.c.x = 3; c = 4; }; - - error: expected a Boolean but found a set: { a = { a = { a = { a = { a = { a = { a = { a = { a = { a = { ... }; b = 2; }; }; }; }; }; }; }; }; «1 attribute elided» }; «1 attribute elided» } - at /pwd/lang/eval-fail-print-limit-set.nix:1:1: - 1| assert ( - | ^ 2| let x = { a.a.a.a.a.a.a.a.a = { a.a = 1; b = 2; }; a.b.c.x = 3; c = 4; }; + | ^ + 3| in builtins.deepSeq x x diff --git a/tests/functional/lang/eval-fail-recursion.err.exp b/tests/functional/lang/eval-fail-recursion.err.exp index 3231a2df9..94cd99cad 100644 --- a/tests/functional/lang/eval-fail-recursion.err.exp +++ b/tests/functional/lang/eval-fail-recursion.err.exp @@ -5,10 +5,16 @@ error: | ^ 2| - … in the right operand of the update (//) operator - at /pwd/lang/eval-fail-recursion.nix:1:12: + … while evaluating a + at /pwd/lang/eval-fail-recursion.nix:1:21: 1| let a = {} // a; in a.foo - | ^ + | ^ + 2| + + … while evaluating a + at /pwd/lang/eval-fail-recursion.nix:1:15: + 1| let a = {} // a; in a.foo + | ^ 2| error: infinite recursion encountered diff --git a/tests/functional/lang/eval-fail-scope-5.err.exp b/tests/functional/lang/eval-fail-scope-5.err.exp index 4dcfb5321..0a04a82bd 100644 --- a/tests/functional/lang/eval-fail-scope-5.err.exp +++ b/tests/functional/lang/eval-fail-scope-5.err.exp @@ -13,6 +13,20 @@ error: | ^ 6| in + … while evaluating x + at /pwd/lang/eval-fail-scope-5.nix:5:23: + 4| + 5| f = {x ? y, y ? x}: x + y; + | ^ + 6| in + + … while evaluating y + at /pwd/lang/eval-fail-scope-5.nix:5:12: + 4| + 5| f = {x ? y, y ? x}: x + y; + | ^ + 6| in + error: infinite recursion encountered at /pwd/lang/eval-fail-scope-5.nix:5:12: 4| diff --git a/tests/functional/repl_characterization/data/regression_9918.test b/tests/functional/repl_characterization/data/regression_9918.test index a85d6d33a..ac20ea6b9 100644 --- a/tests/functional/repl_characterization/data/regression_9918.test +++ b/tests/functional/repl_characterization/data/regression_9918.test @@ -20,6 +20,13 @@ We expect to be able to see locals like r in the debugger: error: … while evaluating the file '$TEST_DATA/regression_9918.nix': + … while evaluating x + at $TEST_DATA/regression_9918.nix:5:3: + 4| in + 5| x + | ^ + 6| + … while calling the 'throw' builtin at $TEST_DATA/regression_9918.nix:3:7: 2| r = []; @@ -27,6 +34,6 @@ We expect to be able to see locals like r in the debugger: | ^ 4| in - … while evaluating the error message passed to builtin.throw + (stack trace truncated; use '--show-trace' to show the full trace) error: cannot coerce a list to a string: [ ] diff --git a/tests/functional/repl_characterization/data/regression_l145.test b/tests/functional/repl_characterization/data/regression_l145.test index bab347d26..491ac5683 100644 --- a/tests/functional/repl_characterization/data/regression_l145.test +++ b/tests/functional/repl_characterization/data/regression_l145.test @@ -16,6 +16,13 @@ debugger should not crash now, but also not show any with variables error: … while evaluating the file '$TEST_DATA/regression_l145.nix': + … while evaluating variable x + at $TEST_DATA/regression_l145.nix:5:3: + 4| in + 5| x + | ^ + 6| + … while calling the 'break' builtin at $TEST_DATA/regression_l145.nix:3:7: 2| let diff --git a/tests/functional/repl_characterization/data/repl_overlays_error.test b/tests/functional/repl_characterization/data/repl_overlays_error.test index 2d8702df1..086136b62 100644 --- a/tests/functional/repl_characterization/data/repl_overlays_error.test +++ b/tests/functional/repl_characterization/data/repl_overlays_error.test @@ -3,19 +3,19 @@ @args ${PWD}/extra_data/repl-overlay-fail.nix @should-start false error: - … while calling the 'foldl'' builtin - at «string»:5:13: - 4| functions: - 5| let final = builtins.foldl' - | ^ - 6| (prev: function: prev // (function info final prev)) + … while evaluating variable final + at «string»:6:1: + 5| in + 6| final + | ^ + 7| - … in the right operand of the update (//) operator - at «string»:6:37: - 5| let final = builtins.foldl' - 6| (prev: function: prev // (function info final prev)) - | ^ - 7| initial + … while calling the 'foldl'' builtin + at «string»:4:11: + 3| let + 4| final = builtins.foldl' (prev: function: prev // (function info final prev)) initial functions; + | ^ + 5| in (stack trace truncated; use '--show-trace' to show the full trace) diff --git a/tests/unit/libexpr/error_traces.cc b/tests/unit/libexpr/error_traces.cc index 673cc56b7..84c1b8733 100644 --- a/tests/unit/libexpr/error_traces.cc +++ b/tests/unit/libexpr/error_traces.cc @@ -779,10 +779,9 @@ namespace nix { TypeError, HintFmt("attempt to call something which is not a function but %s: %s", "an integer", Uncolored(ANSI_CYAN "1" ANSI_NORMAL))); - ASSERT_TRACE2("foldl' (a: b: a && b) \"foo\" [ true ]", + ASSERT_TRACE1("foldl' (a: b: a && b) \"foo\" [ true ]", TypeError, - HintFmt("expected a Boolean but found %s: %s", "a string", Uncolored(ANSI_MAGENTA "\"foo\"" ANSI_NORMAL)), - HintFmt("in the left operand of the AND (&&) operator")); + HintFmt("expected a Boolean but found %s: %s", "a string", Uncolored(ANSI_MAGENTA "\"foo\"" ANSI_NORMAL))); }