From 9d67332e4b8d75a101b8cf6e1d7e4dc8e99e964a Mon Sep 17 00:00:00 2001 From: Geoff Reedy Date: Wed, 22 Sep 2021 20:30:31 -0600 Subject: Better eval error locations for interpolation and + Previously, type or coercion errors for string interpolation, path interpolation, and plus expressions were always reported at the beginning of the outer expression. This leads to confusing evaluation error messages making it hard to accurately diagnose and then fix the error. For example, errors were reported as follows. ``` cannot coerce an integer to a string 1| let foo = 7; in "bar" + foo | ^ cannot add a string to an integer 1| let foo = "bar"; in 4 + foo | ^ cannot coerce an integer to a string 1| let foo = 7; in "x${foo}" | ^ ``` This commit changes the ExprConcatStrings expression vector to store a sequence of expressions *and* their expansion locations so that error locations can be reported accurately. For interpolation, the error is reported at the beginning of the entire `${foo}`, not at the beginning of `foo` because I thought this was slightly clearer. The previous errors are now reported as: ``` cannot coerce an integer to a string 1| let foo = 7; in "bar" + foo | ^ cannot add a string to an integer 1| let foo = "bar"; in 4 + foo | ^ cannot coerce an integer to a string 1| let foo = 7; in "x${foo}" | ^ ``` The error is reported at this kind of precise location even for multi-line indented strings. This probably helps with at least some of the cases mentioned in #561 --- src/libexpr/eval.cc | 8 ++++---- src/libexpr/nixexpr.cc | 4 ++-- src/libexpr/nixexpr.hh | 4 ++-- src/libexpr/parser.y | 38 +++++++++++++++++++------------------- 4 files changed, 27 insertions(+), 27 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index bc41a2cd9..3f18ad1ed 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -1577,7 +1577,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v) bool first = !forceString; ValueType firstType = nString; - for (auto & i : *es) { + for (auto & [i_pos, i] : *es) { Value vTmp; i->eval(state, env, vTmp); @@ -1598,19 +1598,19 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v) nf = n; nf += vTmp.fpoint; } else - throwEvalError(pos, "cannot add %1% to an integer", showType(vTmp)); + throwEvalError(i_pos, "cannot add %1% to an integer", showType(vTmp)); } else if (firstType == nFloat) { if (vTmp.type() == nInt) { nf += vTmp.integer; } else if (vTmp.type() == nFloat) { nf += vTmp.fpoint; } else - throwEvalError(pos, "cannot add %1% to a float", showType(vTmp)); + throwEvalError(i_pos, "cannot add %1% to a float", showType(vTmp)); } else /* skip canonization of first path, which would only be not canonized in the first place if it's coming from a ./${foo} type path */ - s << state.coerceToString(pos, vTmp, context, false, firstType == nString, !first); + s << state.coerceToString(i_pos, vTmp, context, false, firstType == nString, !first); first = false; } diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index 492b819e7..15d963d55 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -181,7 +181,7 @@ void ExprConcatStrings::show(std::ostream & str) const str << "("; for (auto & i : *es) { if (first) first = false; else str << " + "; - str << *i; + str << i.second; } str << ")"; } @@ -413,7 +413,7 @@ void ExprOpNot::bindVars(const StaticEnv & env) void ExprConcatStrings::bindVars(const StaticEnv & env) { for (auto & i : *es) - i->bindVars(env); + i.second->bindVars(env); } void ExprPos::bindVars(const StaticEnv & env) diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index 51a14cd59..d3f7f5bd6 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -321,8 +321,8 @@ struct ExprConcatStrings : Expr { Pos pos; bool forceString; - vector * es; - ExprConcatStrings(const Pos & pos, bool forceString, vector * es) + vector > * es; + ExprConcatStrings(const Pos & pos, bool forceString, vector > * es) : pos(pos), forceString(forceString), es(es) { }; COMMON_METHODS }; diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index e3749783a..c21d7b033 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -154,7 +154,7 @@ static void addFormal(const Pos & pos, Formals * formals, const Formal & formal) } -static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector & es) +static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector > & es) { if (es.empty()) return new ExprString(symbols.create("")); @@ -164,7 +164,7 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector(i); if (!e) { /* Anti-quotations end the current start-of-line whitespace. */ @@ -194,12 +194,12 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector * es2 = new vector; + vector > * es2 = new vector >; atStartOfLine = true; size_t curDropped = 0; size_t n = es.size(); - for (vector::iterator i = es.begin(); i != es.end(); ++i, --n) { - ExprIndStr * e = dynamic_cast(*i); + for (vector >::iterator i = es.begin(); i != es.end(); ++i, --n) { + ExprIndStr * e = dynamic_cast(i->second); if (!e) { atStartOfLine = false; curDropped = 0; @@ -236,11 +236,11 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vectorpush_back(new ExprString(symbols.create(s2))); + es2->emplace_back(i->first, new ExprString(symbols.create(s2))); } /* If this is a single string, then don't do a concatenation. */ - return es2->size() == 1 && dynamic_cast((*es2)[0]) ? (*es2)[0] : new ExprConcatStrings(pos, true, es2); + return es2->size() == 1 && dynamic_cast((*es2)[0].second) ? (*es2)[0].second : new ExprConcatStrings(pos, true, es2); } @@ -279,7 +279,7 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err char * path; char * uri; std::vector * attrNames; - std::vector * string_parts; + std::vector > * string_parts; } %type start expr expr_function expr_if expr_op @@ -366,7 +366,7 @@ expr_op | expr_op UPDATE expr_op { $$ = new ExprOpUpdate(CUR_POS, $1, $3); } | expr_op '?' attrpath { $$ = new ExprOpHasAttr($1, *$3); } | expr_op '+' expr_op - { $$ = new ExprConcatStrings(CUR_POS, false, new vector({$1, $3})); } + { $$ = new ExprConcatStrings(CUR_POS, false, new vector >({{makeCurPos(@1, data), $1}, {makeCurPos(@3, data), $3}})); } | expr_op '-' expr_op { $$ = new ExprApp(CUR_POS, new ExprApp(new ExprVar(data->symbols.create("__sub")), $1), $3); } | expr_op '*' expr_op { $$ = new ExprApp(CUR_POS, new ExprApp(new ExprVar(data->symbols.create("__mul")), $1), $3); } | expr_op '/' expr_op { $$ = new ExprApp(CUR_POS, new ExprApp(new ExprVar(data->symbols.create("__div")), $1), $3); } @@ -407,7 +407,7 @@ expr_simple } | path_start PATH_END { $$ = $1; } | path_start string_parts_interpolated PATH_END { - $2->insert($2->begin(), $1); + $2->insert($2->begin(), {makeCurPos(@1, data), $1}); $$ = new ExprConcatStrings(CUR_POS, false, $2); } | SPATH { @@ -445,13 +445,13 @@ string_parts ; string_parts_interpolated - : string_parts_interpolated STR { $$ = $1; $1->push_back($2); } - | string_parts_interpolated DOLLAR_CURLY expr '}' { $$ = $1; $1->push_back($3); } - | DOLLAR_CURLY expr '}' { $$ = new vector; $$->push_back($2); } + : string_parts_interpolated STR { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $2); } + | string_parts_interpolated DOLLAR_CURLY expr '}' { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $3); } + | DOLLAR_CURLY expr '}' { $$ = new vector >; $$->emplace_back(makeCurPos(@1, data), $2); } | STR DOLLAR_CURLY expr '}' { - $$ = new vector; - $$->push_back($1); - $$->push_back($3); + $$ = new vector >; + $$->emplace_back(makeCurPos(@1, data), $1); + $$->emplace_back(makeCurPos(@2, data), $3); } ; @@ -470,9 +470,9 @@ path_start ; ind_string_parts - : ind_string_parts IND_STR { $$ = $1; $1->push_back($2); } - | ind_string_parts DOLLAR_CURLY expr '}' { $$ = $1; $1->push_back($3); } - | { $$ = new vector; } + : ind_string_parts IND_STR { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $2); } + | ind_string_parts DOLLAR_CURLY expr '}' { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $3); } + | { $$ = new vector >; } ; binds -- cgit v1.2.3 From 7466048d397ed33d4bc28b6910c834cf1a17b0cb Mon Sep 17 00:00:00 2001 From: regnat Date: Mon, 11 Oct 2021 10:47:02 +0200 Subject: (partially) Revert "Don't copy in rethrow" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts some parts of commit 8430a8f0866e4463a891ccce62779ea9ac0f3b38 which was trying to rethrow some exceptions while we weren’t in the context of a `catch` block, causing some weird “terminate called without an active exception” errors. Fix #5368 --- src/libexpr/primops.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/libexpr') diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index f27331534..4e0eda7f3 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -575,7 +575,7 @@ static Bindings::iterator getAttr( // Adding another trace for the function name to make it clear // which call received wrong arguments. e.addTrace(pos, hintfmt("while invoking '%s'", funcName)); - throw; + throw e; } } -- cgit v1.2.3 From 1bdeef83958251e728abe9398b4286cb6b5e9551 Mon Sep 17 00:00:00 2001 From: Kevin Amado Date: Thu, 14 Oct 2021 23:23:04 -0500 Subject: add pos to EvalState::forceValue - This way we improve error messages on infinite recursion - Demo: ```nix let x = builtins.fetchMercurial x; in x ``` - Before: ```bash $ nix-instantiate --show-trace --strict error: infinite recursion encountered ``` - After: ```bash nix-instantiate --show-trace --strict error: infinite recursion encountered at /data/github/kamadorueda/test/default.nix:2:7: 1| let 2| x = builtins.fetchMercurial x; | ^ 3| in ``` Mentions: #3505 --- src/libexpr/primops/fetchMercurial.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/libexpr') diff --git a/src/libexpr/primops/fetchMercurial.cc b/src/libexpr/primops/fetchMercurial.cc index 1cd481243..c23480853 100644 --- a/src/libexpr/primops/fetchMercurial.cc +++ b/src/libexpr/primops/fetchMercurial.cc @@ -15,7 +15,7 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar std::string name = "source"; PathSet context; - state.forceValue(*args[0]); + state.forceValue(*args[0], pos); if (args[0]->type() == nAttrs) { -- cgit v1.2.3 From 18e3d63341bc03e152659a6836a1b52a1241fe57 Mon Sep 17 00:00:00 2001 From: Kevin Amado Date: Fri, 15 Oct 2021 18:56:17 -0500 Subject: fetchTree: add pos to EvalState::forceValue - This way we improve error messages on infinite recursion - Demo: ```nix let x = builtins.fetchTree x; in x ``` - Before: ```bash $ nix-instantiate --extra-experimental-features flakes --strict error: infinite recursion encountered ``` - After: ```bash $ nix-instantiate --extra-experimental-features flakes --strict error: infinite recursion encountered at /data/github/kamadorueda/nix/test.nix:1:9: 1| let x = builtins.fetchTree x; | ^ 2| in x ``` Mentions: #3505 --- src/libexpr/primops/fetchTree.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/libexpr') diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index f570f19ae..441edd648 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -97,7 +97,7 @@ static void fetchTree( fetchers::Input input; PathSet context; - state.forceValue(*args[0]); + state.forceValue(*args[0], pos); if (args[0]->type() == nAttrs) { state.forceAttrs(*args[0], pos); -- cgit v1.2.3 From e5a27a3b4e5ae6a1b84e439f880613e6fe5faf98 Mon Sep 17 00:00:00 2001 From: Kevin Amado Date: Fri, 15 Oct 2021 19:21:38 -0500 Subject: fetchTree: add pos to EvalState::forceValue - This way we improve error messages on infinite recursion - Demo: ```nix let x = builtins.fetchTree { type = "git"; inherit x; }; in x ``` - Before: ```bash $ nix-instantiate --extra-experimental-features flakes --strict error: infinite recursion encountered ``` - After: ```bash $ nix-instantiate --extra-experimental-features flakes --strict error: infinite recursion encountered at /data/github/kamadorueda/nix/test.nix:3:10: 2| type = "git"; 3| inherit x; | ^ 4| }; ``` Mentions: #3505 --- src/libexpr/primops/fetchTree.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/libexpr') diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index 441edd648..727b661e1 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -121,7 +121,7 @@ static void fetchTree( for (auto & attr : *args[0]->attrs) { if (attr.name == state.sType) continue; - state.forceValue(*attr.value); + state.forceValue(*attr.value, *attr.pos); if (attr.value->type() == nPath || attr.value->type() == nString) { auto s = state.coerceToString(*attr.pos, *attr.value, context, false, false); attrs.emplace(attr.name, -- cgit v1.2.3 From 823dce945a702c9ae51a83098f78e55ed780a223 Mon Sep 17 00:00:00 2001 From: Kevin Amado Date: Fri, 15 Oct 2021 21:03:01 -0500 Subject: fetch: nicer infinite recursion errors - This change applies to builtins.fetchurl and builtins.fetchTarball - PoC: `let x = builtins.fetchurl x; in x` - Before: ```bash $ nix-instantiate --extra-experimental-features flakes --strict error: infinite recursion encountered ``` - After: ```bash $ nix-instantiate --extra-experimental-features flakes --strict error: infinite recursion encountered at /data/github/kamadorueda/nix/test.nix:1:9: 1| let x = builtins.fetchurl x; in x | ^ ``` Mentions: #3505 --- src/libexpr/primops/fetchTree.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/libexpr') diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index 727b661e1..b00a79b7f 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -189,7 +189,7 @@ static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v, std::optional url; std::optional expectedHash; - state.forceValue(*args[0]); + state.forceValue(*args[0], pos); if (args[0]->type() == nAttrs) { -- cgit v1.2.3 From ba81e871b2b3a034f3e2eaa5242c98e2f253cdd5 Mon Sep 17 00:00:00 2001 From: Shay Bergmann Date: Mon, 25 Oct 2021 21:13:35 +0000 Subject: toJSON: report error position for fancier output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Given flake: ```nix { description = "nix json error provenance"; inputs = {}; outputs = { self }: { jsonFunction = _: "function"; json = builtins.toJSON (_: "function"); }; } ``` - Before: ```console ❯ nix eval --json .#jsonFunction error: cannot convert a function to JSON ``` - After: ```console ❯ nix eval --json .#jsonFunction error: cannot convert a function to JSON at /nix/store/b7imf1c2j4jnkg3ys7fsfbj02s5j0i4f-source/testflake/flake.nix:4:5: 3| outputs = { self }: { 4| jsonFunction = _: "function"; | ^ 5| json = builtins.toJSON (_: "function"); ``` --- src/libexpr/primops.cc | 4 ++-- src/libexpr/value-to-json.cc | 24 +++++++++++++++--------- src/libexpr/value-to-json.hh | 4 ++-- 3 files changed, 19 insertions(+), 13 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 4e0eda7f3..aac741f90 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1008,7 +1008,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * if (i->name == state.sStructuredAttrs) continue; auto placeholder(jsonObject->placeholder(key)); - printValueAsJSON(state, true, *i->value, placeholder, context); + printValueAsJSON(state, true, *i->value, pos, placeholder, context); if (i->name == state.sBuilder) drv.builder = state.forceString(*i->value, context, posDrvName); @@ -1687,7 +1687,7 @@ static void prim_toJSON(EvalState & state, const Pos & pos, Value * * args, Valu { std::ostringstream out; PathSet context; - printValueAsJSON(state, true, *args[0], out, context); + printValueAsJSON(state, true, *args[0], pos, out, context); mkString(v, out.str(), context); } diff --git a/src/libexpr/value-to-json.cc b/src/libexpr/value-to-json.cc index bfea24d40..7c28754f5 100644 --- a/src/libexpr/value-to-json.cc +++ b/src/libexpr/value-to-json.cc @@ -10,11 +10,11 @@ namespace nix { void printValueAsJSON(EvalState & state, bool strict, - Value & v, JSONPlaceholder & out, PathSet & context) + Value & v, const Pos & pos, JSONPlaceholder & out, PathSet & context) { checkInterrupt(); - if (strict) state.forceValue(v); + if (strict) state.forceValue(v, pos); switch (v.type()) { @@ -54,10 +54,10 @@ void printValueAsJSON(EvalState & state, bool strict, for (auto & j : names) { Attr & a(*v.attrs->find(state.symbols.create(j))); auto placeholder(obj.placeholder(j)); - printValueAsJSON(state, strict, *a.value, placeholder, context); + printValueAsJSON(state, strict, *a.value, *a.pos, placeholder, context); } } else - printValueAsJSON(state, strict, *i->value, out, context); + printValueAsJSON(state, strict, *i->value, *i->pos, out, context); break; } @@ -65,7 +65,7 @@ void printValueAsJSON(EvalState & state, bool strict, auto list(out.list()); for (unsigned int n = 0; n < v.listSize(); ++n) { auto placeholder(list.placeholder()); - printValueAsJSON(state, strict, *v.listElems()[n], placeholder, context); + printValueAsJSON(state, strict, *v.listElems()[n], noPos, placeholder, context); } break; } @@ -79,18 +79,24 @@ void printValueAsJSON(EvalState & state, bool strict, break; case nThunk: - throw TypeError("cannot convert %1% to JSON", showType(v)); + throw TypeError({ + .msg = hintfmt("cannot convert %1% to JSON", showType(v)), + .errPos = pos + }); case nFunction: - throw TypeError("cannot convert %1% to JSON", showType(v)); + throw TypeError({ + .msg = hintfmt("cannot convert %1% to JSON", showType(v)), + .errPos = pos + }); } } void printValueAsJSON(EvalState & state, bool strict, - Value & v, std::ostream & str, PathSet & context) + Value & v, const Pos & pos, std::ostream & str, PathSet & context) { JSONPlaceholder out(str); - printValueAsJSON(state, strict, v, out, context); + printValueAsJSON(state, strict, v, pos, out, context); } void ExternalValueBase::printValueAsJSON(EvalState & state, bool strict, diff --git a/src/libexpr/value-to-json.hh b/src/libexpr/value-to-json.hh index 67fed6487..c2f797b29 100644 --- a/src/libexpr/value-to-json.hh +++ b/src/libexpr/value-to-json.hh @@ -11,9 +11,9 @@ namespace nix { class JSONPlaceholder; void printValueAsJSON(EvalState & state, bool strict, - Value & v, JSONPlaceholder & out, PathSet & context); + Value & v, const Pos & pos, JSONPlaceholder & out, PathSet & context); void printValueAsJSON(EvalState & state, bool strict, - Value & v, std::ostream & str, PathSet & context); + Value & v, const Pos & pos, std::ostream & str, PathSet & context); } -- cgit v1.2.3 From af99941279b80c962ec9cae3e5fa32976a3f5744 Mon Sep 17 00:00:00 2001 From: regnat Date: Mon, 25 Oct 2021 15:53:01 +0200 Subject: Make experimental-features a proper type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rather than having them plain strings scattered through the whole codebase, create an enum containing all the known experimental features. This means that - Nix can now `warn` when an unkwown experimental feature is passed (making it much nicer to spot typos and spot deprecated features) - It’s now easy to remove a feature altogether (once the feature isn’t experimental anymore or is dropped) by just removing the field for the enum and letting the compiler point us to all the now invalid usages of it. --- src/libexpr/eval.cc | 2 +- src/libexpr/eval.hh | 3 ++- src/libexpr/flake/flake.cc | 4 ++-- src/libexpr/parser.y | 2 +- src/libexpr/primops.cc | 2 +- src/libexpr/primops/fetchTree.cc | 10 +++++----- 6 files changed, 12 insertions(+), 11 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 3ae05a8d8..db1e7e56d 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -466,7 +466,7 @@ EvalState::~EvalState() void EvalState::requireExperimentalFeatureOnEvaluation( - const std::string & feature, + const ExperimentalFeature & feature, const std::string_view fName, const Pos & pos) { diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 7cc16ef0a..69119599a 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -5,6 +5,7 @@ #include "nixexpr.hh" #include "symbol-table.hh" #include "config.hh" +#include "experimental-features.hh" #include #include @@ -141,7 +142,7 @@ public: ~EvalState(); void requireExperimentalFeatureOnEvaluation( - const std::string & feature, + const ExperimentalFeature &, const std::string_view fName, const Pos & pos ); diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc index 43bfc3644..c9d848495 100644 --- a/src/libexpr/flake/flake.cc +++ b/src/libexpr/flake/flake.cc @@ -297,7 +297,7 @@ LockedFlake lockFlake( const FlakeRef & topRef, const LockFlags & lockFlags) { - settings.requireExperimentalFeature("flakes"); + settings.requireExperimentalFeature(Xp::Flakes); FlakeCache flakeCache; @@ -687,7 +687,7 @@ void callFlake(EvalState & state, static void prim_getFlake(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.requireExperimentalFeatureOnEvaluation("flakes", "builtins.getFlake", pos); + state.requireExperimentalFeatureOnEvaluation(Xp::Flakes, "builtins.getFlake", pos); auto flakeRefS = state.forceStringNoCtx(*args[0], pos); auto flakeRef = parseFlakeRef(flakeRefS, {}, true); diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 8a0a79c96..380afa6b7 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -418,7 +418,7 @@ expr_simple new ExprString(data->symbols.create(path))); } | URI { - static bool noURLLiterals = settings.isExperimentalFeatureEnabled("no-url-literals"); + static bool noURLLiterals = settings.isExperimentalFeatureEnabled(Xp::NoUrlLiterals); if (noURLLiterals) throw ParseError({ .msg = hintfmt("URL literals are disabled"), diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 4e0eda7f3..6b3cafec8 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -985,7 +985,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * } if (i->name == state.sContentAddressed) { - settings.requireExperimentalFeature("ca-derivations"); + settings.requireExperimentalFeature(Xp::CaDerivations); contentAddressed = state.forceBool(*i->value, pos); } diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index b00a79b7f..dacd78fd3 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -176,7 +176,7 @@ static void fetchTree( static void prim_fetchTree(EvalState & state, const Pos & pos, Value * * args, Value & v) { - settings.requireExperimentalFeature("flakes"); + settings.requireExperimentalFeature(Xp::Flakes); fetchTree(state, pos, args, v, std::nullopt, FetchTreeParams { .allowNameArgument = false }); } @@ -290,7 +290,7 @@ static RegisterPrimOp primop_fetchTarball({ The fetched tarball is cached for a certain amount of time (1 hour by default) in `~/.cache/nix/tarballs/`. You can change the cache timeout either on the command line with `--option tarball-ttl number - of seconds` or in the Nix configuration file with this option: ` + of seconds` or in the Nix configuration file with this option: ` number of seconds to cache `. Note that when obtaining the hash with ` nix-prefetch-url ` the @@ -393,7 +393,7 @@ static RegisterPrimOp primop_fetchGit({ ``` > **Note** - > + > > It is nice to always specify the branch which a revision > belongs to. Without the branch being specified, the fetcher > might fail if the default branch changes. Additionally, it can @@ -430,12 +430,12 @@ static RegisterPrimOp primop_fetchGit({ ``` > **Note** - > + > > Nix will refetch the branch in accordance with > the option `tarball-ttl`. > **Note** - > + > > This behavior is disabled in *Pure evaluation mode*. )", .fun = prim_fetchGit, -- cgit v1.2.3 From 9ce84c64c5820847a8084f31da5c5e2f549cbbcf Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 26 Oct 2021 14:21:24 +0200 Subject: Tweak fetchTree docs --- src/libexpr/primops/fetchTree.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index dacd78fd3..e6becdafc 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -287,13 +287,13 @@ static RegisterPrimOp primop_fetchTarball({ stdenv.mkDerivation { … } ``` - The fetched tarball is cached for a certain amount of time (1 hour - by default) in `~/.cache/nix/tarballs/`. You can change the cache - timeout either on the command line with `--option tarball-ttl number - of seconds` or in the Nix configuration file with this option: ` - number of seconds to cache `. + The fetched tarball is cached for a certain amount of time (1 + hour by default) in `~/.cache/nix/tarballs/`. You can change the + cache timeout either on the command line with `--tarball-ttl` + *number-of-seconds* or in the Nix configuration file by adding + the line `tarball-ttl = ` *number-of-seconds*. - Note that when obtaining the hash with ` nix-prefetch-url ` the + Note that when obtaining the hash with `nix-prefetch-url` the option `--unpack` is required. This function can also verify the contents against a hash. In that -- cgit v1.2.3 From 769de259f0060d55ec8317c109cf12aa4003f7fe Mon Sep 17 00:00:00 2001 From: Shay Bergmann Date: Tue, 26 Oct 2021 14:43:15 +0000 Subject: toJSON: pass pos in case of a list as well --- src/libexpr/value-to-json.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/libexpr') diff --git a/src/libexpr/value-to-json.cc b/src/libexpr/value-to-json.cc index 7c28754f5..1c3849593 100644 --- a/src/libexpr/value-to-json.cc +++ b/src/libexpr/value-to-json.cc @@ -65,7 +65,7 @@ void printValueAsJSON(EvalState & state, bool strict, auto list(out.list()); for (unsigned int n = 0; n < v.listSize(); ++n) { auto placeholder(list.placeholder()); - printValueAsJSON(state, strict, *v.listElems()[n], noPos, placeholder, context); + printValueAsJSON(state, strict, *v.listElems()[n], pos, placeholder, context); } break; } -- cgit v1.2.3 From 5a160171d05c4bde95a7ae75c133912e08186a12 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 27 Oct 2021 18:14:12 +0200 Subject: Remove redundant 'warning:' --- src/libexpr/parser.y | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/libexpr') diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 380afa6b7..813ff2fc3 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -752,7 +752,7 @@ std::pair EvalState::resolveSearchPathElem(const SearchPathEl res = { true, path }; else { logWarning({ - .msg = hintfmt("warning: Nix search path entry '%1%' does not exist, ignoring", elem.second) + .msg = hintfmt("Nix search path entry '%1%' does not exist, ignoring", elem.second) }); res = { false, "" }; } -- cgit v1.2.3 From a50c027ece2dc853109c7d2c2f10dd6581e2554d Mon Sep 17 00:00:00 2001 From: Shay Bergmann Date: Wed, 27 Oct 2021 19:48:48 +0000 Subject: toJSON: improve pos accuracy, add trace --- src/libexpr/value-to-json.cc | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/value-to-json.cc b/src/libexpr/value-to-json.cc index 1c3849593..4d642c720 100644 --- a/src/libexpr/value-to-json.cc +++ b/src/libexpr/value-to-json.cc @@ -40,7 +40,7 @@ void printValueAsJSON(EvalState & state, bool strict, break; case nAttrs: { - auto maybeString = state.tryAttrsToString(noPos, v, context, false, false); + auto maybeString = state.tryAttrsToString(pos, v, context, false, false); if (maybeString) { out.write(*maybeString); break; @@ -79,16 +79,12 @@ void printValueAsJSON(EvalState & state, bool strict, break; case nThunk: - throw TypeError({ - .msg = hintfmt("cannot convert %1% to JSON", showType(v)), - .errPos = pos - }); - case nFunction: - throw TypeError({ + auto e = TypeError({ .msg = hintfmt("cannot convert %1% to JSON", showType(v)), - .errPos = pos + .errPos = v.determinePos(pos) }); + throw e.addTrace(pos, hintfmt("message for the trace")); } } -- cgit v1.2.3 From 5b2aa61f1b8fe253c963874ccb4700f6ff99526e Mon Sep 17 00:00:00 2001 From: regnat Date: Tue, 26 Oct 2021 16:55:57 +0200 Subject: =?UTF-8?q?Don=E2=80=99t=20require=20`ca-derivations`=20when=20`?= =?UTF-8?q?=5F=5FcontentAddressed=20=3D=20false`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If we explicitely opt-out of it, there’s no need to require the experimental feature --- src/libexpr/primops.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/libexpr') diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 6b3cafec8..f05cca169 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -985,8 +985,9 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * } if (i->name == state.sContentAddressed) { - settings.requireExperimentalFeature(Xp::CaDerivations); contentAddressed = state.forceBool(*i->value, pos); + if (contentAddressed) + settings.requireExperimentalFeature(Xp::CaDerivations); } /* The `args' attribute is special: it supplies the -- cgit v1.2.3 From ab35cbd675610a52513f746051e98d0a50815bc1 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 21 Feb 2020 18:31:16 +0100 Subject: StaticEnv: Use std::vector instead of std::map --- src/libexpr/eval.cc | 6 +++--- src/libexpr/nixexpr.cc | 25 +++++++++++++++++-------- src/libexpr/nixexpr.hh | 25 +++++++++++++++++++++---- src/libexpr/parser.y | 8 ++++---- src/libexpr/primops.cc | 10 ++++++++-- 5 files changed, 53 insertions(+), 21 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index db1e7e56d..062d190b6 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -583,7 +583,7 @@ Value * EvalState::addConstant(const string & name, Value & v) { Value * v2 = allocValue(); *v2 = v; - staticBaseEnv.vars[symbols.create(name)] = baseEnvDispl; + staticBaseEnv.vars.emplace_back(symbols.create(name), baseEnvDispl); baseEnv.values[baseEnvDispl++] = v2; string name2 = string(name, 0, 2) == "__" ? string(name, 2) : name; baseEnv.values[0]->attrs->push_back(Attr(symbols.create(name2), v2)); @@ -609,7 +609,7 @@ Value * EvalState::addPrimOp(const string & name, Value * v = allocValue(); v->mkPrimOp(new PrimOp { .fun = primOp, .arity = arity, .name = sym }); - staticBaseEnv.vars[symbols.create(name)] = baseEnvDispl; + staticBaseEnv.vars.emplace_back(symbols.create(name), baseEnvDispl); baseEnv.values[baseEnvDispl++] = v; baseEnv.values[0]->attrs->push_back(Attr(sym, v)); return v; @@ -635,7 +635,7 @@ Value * EvalState::addPrimOp(PrimOp && primOp) Value * v = allocValue(); v->mkPrimOp(new PrimOp(std::move(primOp))); - staticBaseEnv.vars[envName] = baseEnvDispl; + staticBaseEnv.vars.emplace_back(envName, baseEnvDispl); baseEnv.values[baseEnvDispl++] = v; baseEnv.values[0]->attrs->push_back(Attr(primOp.name, v)); return v; diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index 0d0f3e469..95a353a40 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -269,7 +269,7 @@ void ExprVar::bindVars(const StaticEnv & env) if (curEnv->isWith) { if (withLevel == -1) withLevel = level; } else { - StaticEnv::Vars::const_iterator i = curEnv->vars.find(name); + auto i = curEnv->find(name); if (i != curEnv->vars.end()) { fromWith = false; this->level = level; @@ -311,14 +311,16 @@ void ExprOpHasAttr::bindVars(const StaticEnv & env) void ExprAttrs::bindVars(const StaticEnv & env) { const StaticEnv * dynamicEnv = &env; - StaticEnv newEnv(false, &env); + StaticEnv newEnv(false, &env, recursive ? attrs.size() : 0); if (recursive) { dynamicEnv = &newEnv; unsigned int displ = 0; for (auto & i : attrs) - newEnv.vars[i.first] = i.second.displ = displ++; + newEnv.vars.emplace_back(i.first, i.second.displ = displ++); + + // No need to sort newEnv since attrs is in sorted order. for (auto & i : attrs) i.second.e->bindVars(i.second.inherited ? env : newEnv); @@ -342,15 +344,20 @@ void ExprList::bindVars(const StaticEnv & env) void ExprLambda::bindVars(const StaticEnv & env) { - StaticEnv newEnv(false, &env); + StaticEnv newEnv( + false, &env, + (hasFormals() ? formals->formals.size() : 0) + + (arg.empty() ? 0 : 1)); unsigned int displ = 0; - if (!arg.empty()) newEnv.vars[arg] = displ++; + if (!arg.empty()) newEnv.vars.emplace_back(arg, displ++); if (hasFormals()) { for (auto & i : formals->formals) - newEnv.vars[i.name] = displ++; + newEnv.vars.emplace_back(i.name, displ++); + + newEnv.sort(); for (auto & i : formals->formals) if (i.def) i.def->bindVars(newEnv); @@ -361,11 +368,13 @@ void ExprLambda::bindVars(const StaticEnv & env) void ExprLet::bindVars(const StaticEnv & env) { - StaticEnv newEnv(false, &env); + StaticEnv newEnv(false, &env, attrs->attrs.size()); unsigned int displ = 0; for (auto & i : attrs->attrs) - newEnv.vars[i.first] = i.second.displ = displ++; + newEnv.vars.emplace_back(i.first, i.second.displ = displ++); + + // No need to sort newEnv since attrs->attrs is in sorted order. for (auto & i : attrs->attrs) i.second.e->bindVars(i.second.inherited ? env : newEnv); diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index 851e875bd..3bfa5e0d3 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -4,8 +4,6 @@ #include "symbol-table.hh" #include "error.hh" -#include - namespace nix { @@ -342,9 +340,28 @@ struct StaticEnv { bool isWith; const StaticEnv * up; - typedef std::map Vars; + + // Note: these must be in sorted order. + typedef std::vector> Vars; Vars vars; - StaticEnv(bool isWith, const StaticEnv * up) : isWith(isWith), up(up) { }; + + StaticEnv(bool isWith, const StaticEnv * up, size_t expectedSize = 0) : isWith(isWith), up(up) { + vars.reserve(expectedSize); + }; + + void sort() + { + std::sort(vars.begin(), vars.end(), + [](const Vars::value_type & a, const Vars::value_type & b) { return a.first < b.first; }); + } + + Vars::const_iterator find(const Symbol & name) const + { + Vars::value_type key(name, 0); + auto i = std::lower_bound(vars.begin(), vars.end(), key); + if (i != vars.end() && i->first == name) return i; + return vars.end(); + } }; diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 813ff2fc3..5d0f05206 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -126,14 +126,14 @@ static void addAttr(ExprAttrs * attrs, AttrPath & attrPath, auto j2 = jAttrs->attrs.find(ad.first); if (j2 != jAttrs->attrs.end()) // Attr already defined in iAttrs, error. dupAttr(ad.first, j2->second.pos, ad.second.pos); - jAttrs->attrs[ad.first] = ad.second; + jAttrs->attrs.emplace(ad.first, ad.second); } } else { dupAttr(attrPath, pos, j->second.pos); } } else { // This attr path is not defined. Let's create it. - attrs->attrs[i->symbol] = ExprAttrs::AttrDef(e, pos); + attrs->attrs.emplace(i->symbol, ExprAttrs::AttrDef(e, pos)); e->setName(i->symbol); } } else { @@ -483,7 +483,7 @@ binds if ($$->attrs.find(i.symbol) != $$->attrs.end()) dupAttr(i.symbol, makeCurPos(@3, data), $$->attrs[i.symbol].pos); Pos pos = makeCurPos(@3, data); - $$->attrs[i.symbol] = ExprAttrs::AttrDef(new ExprVar(CUR_POS, i.symbol), pos, true); + $$->attrs.emplace(i.symbol, ExprAttrs::AttrDef(new ExprVar(CUR_POS, i.symbol), pos, true)); } } | binds INHERIT '(' expr ')' attrs ';' @@ -492,7 +492,7 @@ binds for (auto & i : *$6) { if ($$->attrs.find(i.symbol) != $$->attrs.end()) dupAttr(i.symbol, makeCurPos(@6, data), $$->attrs[i.symbol].pos); - $$->attrs[i.symbol] = ExprAttrs::AttrDef(new ExprSelect(CUR_POS, $4, i.symbol), makeCurPos(@6, data)); + $$->attrs.emplace(i.symbol, ExprAttrs::AttrDef(new ExprSelect(CUR_POS, $4, i.symbol), makeCurPos(@6, data))); } } | { $$ = new ExprAttrs(makeCurPos(@0, data)); } diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 6b3cafec8..8a573f35c 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -184,14 +184,17 @@ static void import(EvalState & state, const Pos & pos, Value & vPath, Value * vS Env * env = &state.allocEnv(vScope->attrs->size()); env->up = &state.baseEnv; - StaticEnv staticEnv(false, &state.staticBaseEnv); + StaticEnv staticEnv(false, &state.staticBaseEnv, vScope->attrs->size()); unsigned int displ = 0; for (auto & attr : *vScope->attrs) { - staticEnv.vars[attr.name] = displ; + staticEnv.vars.emplace_back(attr.name, displ); env->values[displ++] = attr.value; } + // No need to call staticEnv.sort(), because + // args[0]->attrs is already sorted. + printTalkative("evaluating file '%1%'", realPath); Expr * e = state.parseExprFromFile(resolveExprPath(realPath), staticEnv); @@ -3726,6 +3729,7 @@ void EvalState::createBaseEnv() /* Add a wrapper around the derivation primop that computes the `drvPath' and `outPath' attributes lazily. */ + staticBaseEnv.sort(); sDerivationNix = symbols.create("//builtin/derivation.nix"); eval(parse( #include "primops/derivation.nix.gen.hh" @@ -3735,6 +3739,8 @@ void EvalState::createBaseEnv() /* Now that we've added all primops, sort the `builtins' set, because attribute lookups expect it to be sorted. */ baseEnv.values[0]->attrs->sort(); + + staticBaseEnv.sort(); } -- cgit v1.2.3 From 81e7c40264520b387358917987d101f5f5ae4705 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 24 Feb 2020 01:32:01 +0100 Subject: Optimize primop calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We now parse function applications as a vector of arguments rather than as a chain of binary applications, e.g. 'substring 1 2 "foo"' is parsed as ExprCall { .fun = , .args = [ <1>, <2>, <"foo"> ] } rather than ExprApp (ExprApp (ExprApp <1>) <2>) <"foo"> This allows primops to be called immediately (if enough arguments are supplied) without having to allocate intermediate tPrimOpApp values. On $ nix-instantiate --dry-run '' -A nixos.tests.simple.x86_64-linux this gives a substantial performance improvement: user CPU time: median = 0.9209 mean = 0.9218 stddev = 0.0073 min = 0.9086 max = 0.9340 [rejected, p=0.00000, Δ=-0.21433±0.00677] elapsed time: median = 1.0585 mean = 1.0584 stddev = 0.0024 min = 1.0523 max = 1.0623 [rejected, p=0.00000, Δ=-0.20594±0.00236] because it reduces the number of tPrimOpApp allocations from 551990 to 42534 (i.e. only small minority of primop calls are partially applied) which in turn reduces time spent in the garbage collector. --- src/libexpr/eval.cc | 274 ++++++++++++++++++++++++++++--------------------- src/libexpr/eval.hh | 12 ++- src/libexpr/lexer.l | 1 + src/libexpr/nixexpr.cc | 18 +++- src/libexpr/nixexpr.hh | 12 ++- src/libexpr/parser.y | 51 +++++---- 6 files changed, 229 insertions(+), 139 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 062d190b6..b4b4f7b5c 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -594,6 +594,8 @@ Value * EvalState::addConstant(const string & name, Value & v) Value * EvalState::addPrimOp(const string & name, size_t arity, PrimOpFun primOp) { + assert(arity <= maxPrimOpArity); + auto name2 = string(name, 0, 2) == "__" ? string(name, 2) : name; Symbol sym = symbols.create(name2); @@ -1251,144 +1253,182 @@ void ExprLambda::eval(EvalState & state, Env & env, Value & v) } -void ExprApp::eval(EvalState & state, Env & env, Value & v) +void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & vRes, const Pos & pos) { - /* FIXME: vFun prevents GCC from doing tail call optimisation. */ - Value vFun; - e1->eval(state, env, vFun); - state.callFunction(vFun, *(e2->maybeThunk(state, env)), v, pos); -} + auto trace = evalSettings.traceFunctionCalls ? std::make_unique(pos) : nullptr; + forceValue(fun, pos); -void EvalState::callPrimOp(Value & fun, Value & arg, Value & v, const Pos & pos) -{ - /* Figure out the number of arguments still needed. */ - size_t argsDone = 0; - Value * primOp = &fun; - while (primOp->isPrimOpApp()) { - argsDone++; - primOp = primOp->primOpApp.left; - } - assert(primOp->isPrimOp()); - auto arity = primOp->primOp->arity; - auto argsLeft = arity - argsDone; - - if (argsLeft == 1) { - /* We have all the arguments, so call the primop. */ - - /* Put all the arguments in an array. */ - Value * vArgs[arity]; - auto n = arity - 1; - vArgs[n--] = &arg; - for (Value * arg = &fun; arg->isPrimOpApp(); arg = arg->primOpApp.left) - vArgs[n--] = arg->primOpApp.right; - - /* And call the primop. */ - nrPrimOpCalls++; - if (countCalls) primOpCalls[primOp->primOp->name]++; - primOp->primOp->fun(*this, pos, vArgs, v); - } else { - Value * fun2 = allocValue(); - *fun2 = fun; - v.mkPrimOpApp(fun2, &arg); - } -} + Value vCur(fun); -void EvalState::callFunction(Value & fun, Value & arg, Value & v, const Pos & pos) -{ - auto trace = evalSettings.traceFunctionCalls ? std::make_unique(pos) : nullptr; + auto makeAppChain = [&]() + { + vRes = vCur; + for (size_t i = 0; i < nrArgs; ++i) { + auto fun2 = allocValue(); + *fun2 = vRes; + vRes.mkPrimOpApp(fun2, args[i]); + } + }; - forceValue(fun, pos); + while (nrArgs > 0) { - if (fun.isPrimOp() || fun.isPrimOpApp()) { - callPrimOp(fun, arg, v, pos); - return; - } + if (vCur.isLambda()) { - if (fun.type() == nAttrs) { - auto found = fun.attrs->find(sFunctor); - if (found != fun.attrs->end()) { - /* fun may be allocated on the stack of the calling function, - * but for functors we may keep a reference, so heap-allocate - * a copy and use that instead. - */ - auto & fun2 = *allocValue(); - fun2 = fun; - /* !!! Should we use the attr pos here? */ - Value v2; - callFunction(*found->value, fun2, v2, pos); - return callFunction(v2, arg, v, pos); - } - } + ExprLambda & lambda(*vCur.lambda.fun); - if (!fun.isLambda()) - throwTypeError(pos, "attempt to call something which is not a function but %1%", fun); + auto size = + (lambda.arg.empty() ? 0 : 1) + + (lambda.hasFormals() ? lambda.formals->formals.size() : 0); + Env & env2(allocEnv(size)); + env2.up = vCur.lambda.env; - ExprLambda & lambda(*fun.lambda.fun); + size_t displ = 0; - auto size = - (lambda.arg.empty() ? 0 : 1) + - (lambda.hasFormals() ? lambda.formals->formals.size() : 0); - Env & env2(allocEnv(size)); - env2.up = fun.lambda.env; + if (!lambda.hasFormals()) + env2.values[displ++] = args[0]; - size_t displ = 0; + else { + forceAttrs(*args[0], pos); - if (!lambda.hasFormals()) - env2.values[displ++] = &arg; + if (!lambda.arg.empty()) + env2.values[displ++] = args[0]; - else { - forceAttrs(arg, pos); - - if (!lambda.arg.empty()) - env2.values[displ++] = &arg; - - /* For each formal argument, get the actual argument. If - there is no matching actual argument but the formal - argument has a default, use the default. */ - size_t attrsUsed = 0; - for (auto & i : lambda.formals->formals) { - Bindings::iterator j = arg.attrs->find(i.name); - if (j == arg.attrs->end()) { - if (!i.def) throwTypeError(pos, "%1% called without required argument '%2%'", - lambda, i.name); - env2.values[displ++] = i.def->maybeThunk(*this, env2); + /* For each formal argument, get the actual argument. If + there is no matching actual argument but the formal + argument has a default, use the default. */ + size_t attrsUsed = 0; + for (auto & i : lambda.formals->formals) { + auto j = args[0]->attrs->get(i.name); + if (!j) { + if (!i.def) throwTypeError(pos, "%1% called without required argument '%2%'", + lambda, i.name); + env2.values[displ++] = i.def->maybeThunk(*this, env2); + } else { + attrsUsed++; + env2.values[displ++] = j->value; + } + } + + /* Check that each actual argument is listed as a formal + argument (unless the attribute match specifies a `...'). */ + if (!lambda.formals->ellipsis && attrsUsed != args[0]->attrs->size()) { + /* Nope, so show the first unexpected argument to the + user. */ + for (auto & i : *args[0]->attrs) + if (lambda.formals->argNames.find(i.name) == lambda.formals->argNames.end()) + throwTypeError(pos, "%1% called with unexpected argument '%2%'", lambda, i.name); + abort(); // can't happen + } + } + + nrFunctionCalls++; + if (countCalls) incrFunctionCall(&lambda); + + /* Evaluate the body. */ + try { + lambda.body->eval(*this, env2, vCur); + } catch (Error & e) { + if (loggerSettings.showTrace.get()) { + addErrorTrace(e, lambda.pos, "while evaluating %s", + (lambda.name.set() + ? "'" + (string) lambda.name + "'" + : "anonymous lambda")); + addErrorTrace(e, pos, "from call site%s", ""); + } + throw; + } + + nrArgs--; + args += 1; + } + + else if (vCur.isPrimOp()) { + + size_t argsLeft = vCur.primOp->arity; + + if (nrArgs < argsLeft) { + /* We don't have enough arguments, so create a tPrimOpApp chain. */ + makeAppChain(); + return; } else { - attrsUsed++; - env2.values[displ++] = j->value; + /* We have all the arguments, so call the primop. */ + nrPrimOpCalls++; + if (countCalls) primOpCalls[vCur.primOp->name]++; + vCur.primOp->fun(*this, pos, args, vCur); + + nrArgs -= argsLeft; + args += argsLeft; + } + } + + else if (vCur.isPrimOpApp()) { + /* Figure out the number of arguments still needed. */ + size_t argsDone = 0; + Value * primOp = &vCur; + while (primOp->isPrimOpApp()) { + argsDone++; + primOp = primOp->primOpApp.left; + } + assert(primOp->isPrimOp()); + auto arity = primOp->primOp->arity; + auto argsLeft = arity - argsDone; + + if (nrArgs < argsLeft) { + /* We still don't have enough arguments, so extend the tPrimOpApp chain. */ + makeAppChain(); + return; + } else { + /* We have all the arguments, so call the primop with + the previous and new arguments. */ + + Value * vArgs[arity]; + auto n = argsDone; + for (Value * arg = &vCur; arg->isPrimOpApp(); arg = arg->primOpApp.left) + vArgs[--n] = arg->primOpApp.right; + + for (size_t i = 0; i < argsLeft; ++i) + vArgs[argsDone + i] = args[i]; + + nrPrimOpCalls++; + if (countCalls) primOpCalls[primOp->primOp->name]++; + primOp->primOp->fun(*this, pos, vArgs, vCur); + + nrArgs -= argsLeft; + args += argsLeft; } } - /* Check that each actual argument is listed as a formal - argument (unless the attribute match specifies a `...'). */ - if (!lambda.formals->ellipsis && attrsUsed != arg.attrs->size()) { - /* Nope, so show the first unexpected argument to the - user. */ - for (auto & i : *arg.attrs) - if (lambda.formals->argNames.find(i.name) == lambda.formals->argNames.end()) - throwTypeError(pos, "%1% called with unexpected argument '%2%'", lambda, i.name); - abort(); // can't happen + else if (vCur.type() == nAttrs) { + if (auto functor = vCur.attrs->get(sFunctor)) { + /* 'vCur" may be allocated on the stack of the calling + function, but for functors we may keep a reference, + so heap-allocate a copy and use that instead. */ + Value * args2[] = {allocValue()}; + *args2[0] = vCur; + /* !!! Should we use the attr pos here? */ + callFunction(*functor->value, 1, args2, vCur, pos); + } } + + else + throwTypeError(pos, "attempt to call something which is not a function but %1%", vCur); } - nrFunctionCalls++; - if (countCalls) incrFunctionCall(&lambda); + vRes = vCur; +} - /* Evaluate the body. This is conditional on showTrace, because - catching exceptions makes this function not tail-recursive. */ - if (loggerSettings.showTrace.get()) - try { - lambda.body->eval(*this, env2, v); - } catch (Error & e) { - addErrorTrace(e, lambda.pos, "while evaluating %s", - (lambda.name.set() - ? "'" + (string) lambda.name + "'" - : "anonymous lambda")); - addErrorTrace(e, pos, "from call site%s", ""); - throw; - } - else - fun.lambda.fun->body->eval(*this, env2, v); + +void ExprCall::eval(EvalState & state, Env & env, Value & v) +{ + Value vFun; + fun->eval(state, env, vFun); + + Value * vArgs[args.size()]; + for (size_t i = 0; i < args.size(); ++i) + vArgs[i] = args[i]->maybeThunk(state, env); + + state.callFunction(vFun, args.size(), vArgs, v, pos); } diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 69119599a..f87dcdd8e 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -277,6 +277,8 @@ private: Value * addConstant(const string & name, Value & v); + constexpr static size_t maxPrimOpArity = 3; + Value * addPrimOp(const string & name, size_t arity, PrimOpFun primOp); @@ -316,8 +318,14 @@ public: bool isFunctor(Value & fun); - void callFunction(Value & fun, Value & arg, Value & v, const Pos & pos); - void callPrimOp(Value & fun, Value & arg, Value & v, const Pos & pos); + // FIXME: use std::span + void callFunction(Value & fun, size_t nrArgs, Value * * args, Value & vRes, const Pos & pos); + + void callFunction(Value & fun, Value & arg, Value & vRes, const Pos & pos) + { + Value * args[] = {&arg}; + callFunction(fun, 1, args, vRes, pos); + } /* Automatically call a function for which each argument has a default value or has a binding in the `args' map. */ diff --git a/src/libexpr/lexer.l b/src/libexpr/lexer.l index 51593eccd..c18877e29 100644 --- a/src/libexpr/lexer.l +++ b/src/libexpr/lexer.l @@ -64,6 +64,7 @@ static void adjustLoc(YYLTYPE * loc, const char * s, size_t len) } +// FIXME: optimize static Expr * unescapeStr(SymbolTable & symbols, const char * s, size_t length) { string t; diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index 95a353a40..372e323bc 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -143,6 +143,16 @@ void ExprLambda::show(std::ostream & str) const str << ": " << *body << ")"; } +void ExprCall::show(std::ostream & str) const +{ + str << '(' << *fun; + for (auto e : args) { + str << ' '; + str << *e; + } + str << ')'; +} + void ExprLet::show(std::ostream & str) const { str << "(let "; @@ -366,6 +376,13 @@ void ExprLambda::bindVars(const StaticEnv & env) body->bindVars(newEnv); } +void ExprCall::bindVars(const StaticEnv & env) +{ + fun->bindVars(env); + for (auto e : args) + e->bindVars(env); +} + void ExprLet::bindVars(const StaticEnv & env) { StaticEnv newEnv(false, &env, attrs->attrs.size()); @@ -461,5 +478,4 @@ size_t SymbolTable::totalSize() const return n; } - } diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index 3bfa5e0d3..fe12890e9 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -248,6 +248,17 @@ struct ExprLambda : Expr COMMON_METHODS }; +struct ExprCall : Expr +{ + Expr * fun; + std::vector args; + Pos pos; + ExprCall(const Pos & pos, Expr * fun, std::vector && args) + : fun(fun), args(args), pos(pos) + { } + COMMON_METHODS +}; + struct ExprLet : Expr { ExprAttrs * attrs; @@ -306,7 +317,6 @@ struct ExprOpNot : Expr void eval(EvalState & state, Env & env, Value & v); \ }; -MakeBinOp(ExprApp, "") MakeBinOp(ExprOpEq, "==") MakeBinOp(ExprOpNEq, "!=") MakeBinOp(ExprOpAnd, "&&") diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 5d0f05206..2e8a04143 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -41,6 +41,12 @@ namespace nix { { }; }; + // Helper to prevent an expensive dynamic_cast call in expr_app. + struct App + { + Expr * e; + bool isCall; + }; } #define YY_DECL int yylex \ @@ -280,10 +286,12 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err char * uri; std::vector * attrNames; std::vector * string_parts; + nix::App app; // bool == whether this is an ExprCall } %type start expr expr_function expr_if expr_op -%type expr_app expr_select expr_simple +%type expr_select expr_simple +%type expr_app %type expr_list %type binds %type formals @@ -353,13 +361,13 @@ expr_if expr_op : '!' expr_op %prec NOT { $$ = new ExprOpNot($2); } - | '-' expr_op %prec NEGATE { $$ = new ExprApp(CUR_POS, new ExprApp(new ExprVar(data->symbols.create("__sub")), new ExprInt(0)), $2); } + | '-' expr_op %prec NEGATE { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__sub")), {new ExprInt(0), $2}); } | expr_op EQ expr_op { $$ = new ExprOpEq($1, $3); } | expr_op NEQ expr_op { $$ = new ExprOpNEq($1, $3); } - | expr_op '<' expr_op { $$ = new ExprApp(CUR_POS, new ExprApp(new ExprVar(data->symbols.create("__lessThan")), $1), $3); } - | expr_op LEQ expr_op { $$ = new ExprOpNot(new ExprApp(CUR_POS, new ExprApp(new ExprVar(data->symbols.create("__lessThan")), $3), $1)); } - | expr_op '>' expr_op { $$ = new ExprApp(CUR_POS, new ExprApp(new ExprVar(data->symbols.create("__lessThan")), $3), $1); } - | expr_op GEQ expr_op { $$ = new ExprOpNot(new ExprApp(CUR_POS, new ExprApp(new ExprVar(data->symbols.create("__lessThan")), $1), $3)); } + | expr_op '<' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__lessThan")), {$1, $3}); } + | expr_op LEQ expr_op { $$ = new ExprOpNot(new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__lessThan")), {$3, $1})); } + | expr_op '>' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__lessThan")), {$3, $1}); } + | expr_op GEQ expr_op { $$ = new ExprOpNot(new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__lessThan")), {$1, $3})); } | expr_op AND expr_op { $$ = new ExprOpAnd(CUR_POS, $1, $3); } | expr_op OR expr_op { $$ = new ExprOpOr(CUR_POS, $1, $3); } | expr_op IMPL expr_op { $$ = new ExprOpImpl(CUR_POS, $1, $3); } @@ -367,17 +375,24 @@ expr_op | expr_op '?' attrpath { $$ = new ExprOpHasAttr($1, *$3); } | expr_op '+' expr_op { $$ = new ExprConcatStrings(CUR_POS, false, new vector({$1, $3})); } - | expr_op '-' expr_op { $$ = new ExprApp(CUR_POS, new ExprApp(new ExprVar(data->symbols.create("__sub")), $1), $3); } - | expr_op '*' expr_op { $$ = new ExprApp(CUR_POS, new ExprApp(new ExprVar(data->symbols.create("__mul")), $1), $3); } - | expr_op '/' expr_op { $$ = new ExprApp(CUR_POS, new ExprApp(new ExprVar(data->symbols.create("__div")), $1), $3); } + | expr_op '-' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__sub")), {$1, $3}); } + | expr_op '*' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__mul")), {$1, $3}); } + | expr_op '/' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__div")), {$1, $3}); } | expr_op CONCAT expr_op { $$ = new ExprOpConcatLists(CUR_POS, $1, $3); } - | expr_app + | expr_app { $$ = $1.e; } ; expr_app - : expr_app expr_select - { $$ = new ExprApp(CUR_POS, $1, $2); } - | expr_select { $$ = $1; } + : expr_app expr_select { + if ($1.isCall) { + ((ExprCall *) $1.e)->args.push_back($2); + $$ = $1; + } else { + $$.e = new ExprCall(CUR_POS, $1.e, {$2}); + $$.isCall = true; + } + } + | expr_select { $$.e = $1; $$.isCall = false; } ; expr_select @@ -388,7 +403,7 @@ expr_select | /* Backwards compatibility: because Nixpkgs has a rarely used function named ‘or’, allow stuff like ‘map or [...]’. */ expr_simple OR_KW - { $$ = new ExprApp(CUR_POS, $1, new ExprVar(CUR_POS, data->symbols.create("or"))); } + { $$ = new ExprCall(CUR_POS, $1, {new ExprVar(CUR_POS, data->symbols.create("or"))}); } | expr_simple { $$ = $1; } ; @@ -412,10 +427,10 @@ expr_simple } | SPATH { string path($1 + 1, strlen($1) - 2); - $$ = new ExprApp(CUR_POS, - new ExprApp(new ExprVar(data->symbols.create("__findFile")), - new ExprVar(data->symbols.create("__nixPath"))), - new ExprString(data->symbols.create(path))); + $$ = new ExprCall(CUR_POS, + new ExprVar(data->symbols.create("__findFile")), + {new ExprVar(data->symbols.create("__nixPath")), + new ExprString(data->symbols.create(path))}); } | URI { static bool noURLLiterals = settings.isExperimentalFeatureEnabled(Xp::NoUrlLiterals); -- cgit v1.2.3 From bcf47800067243b124569da9019077c81bf71cd5 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 24 Feb 2020 14:33:01 +0100 Subject: Add level / displacement types --- src/libexpr/eval.cc | 8 ++++---- src/libexpr/nixexpr.cc | 10 +++++----- src/libexpr/nixexpr.hh | 11 +++++++---- 3 files changed, 16 insertions(+), 13 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index b4b4f7b5c..641687f2a 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -787,7 +787,7 @@ void mkPath(Value & v, const char * s) inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval) { - for (size_t l = var.level; l; --l, env = env->up) ; + for (auto l = var.level; l; --l, env = env->up) ; if (!var.fromWith) return env->values[var.displ]; @@ -1060,7 +1060,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v) /* The recursive attributes are evaluated in the new environment, while the inherited attributes are evaluated in the original environment. */ - size_t displ = 0; + Displacement displ = 0; for (auto & i : attrs) { Value * vAttr; if (hasOverrides && !i.second.inherited) { @@ -1136,7 +1136,7 @@ void ExprLet::eval(EvalState & state, Env & env, Value & v) /* The recursive attributes are evaluated in the new environment, while the inherited attributes are evaluated in the original environment. */ - size_t displ = 0; + Displacement displ = 0; for (auto & i : attrs->attrs) env2.values[displ++] = i.second.e->maybeThunk(state, i.second.inherited ? env : env2); @@ -1283,7 +1283,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & Env & env2(allocEnv(size)); env2.up = vCur.lambda.env; - size_t displ = 0; + Displacement displ = 0; if (!lambda.hasFormals()) env2.values[displ++] = args[0]; diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index 372e323bc..57c2f6e44 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -273,7 +273,7 @@ void ExprVar::bindVars(const StaticEnv & env) /* Check whether the variable appears in the environment. If so, set its level and displacement. */ const StaticEnv * curEnv; - unsigned int level; + Level level; int withLevel = -1; for (curEnv = &env, level = 0; curEnv; curEnv = curEnv->up, level++) { if (curEnv->isWith) { @@ -326,7 +326,7 @@ void ExprAttrs::bindVars(const StaticEnv & env) if (recursive) { dynamicEnv = &newEnv; - unsigned int displ = 0; + Displacement displ = 0; for (auto & i : attrs) newEnv.vars.emplace_back(i.first, i.second.displ = displ++); @@ -359,7 +359,7 @@ void ExprLambda::bindVars(const StaticEnv & env) (hasFormals() ? formals->formals.size() : 0) + (arg.empty() ? 0 : 1)); - unsigned int displ = 0; + Displacement displ = 0; if (!arg.empty()) newEnv.vars.emplace_back(arg, displ++); @@ -387,7 +387,7 @@ void ExprLet::bindVars(const StaticEnv & env) { StaticEnv newEnv(false, &env, attrs->attrs.size()); - unsigned int displ = 0; + Displacement displ = 0; for (auto & i : attrs->attrs) newEnv.vars.emplace_back(i.first, i.second.displ = displ++); @@ -405,7 +405,7 @@ void ExprWith::bindVars(const StaticEnv & env) level so that `lookupVar' can look up variables in the previous `with' if this one doesn't contain the desired attribute. */ const StaticEnv * curEnv; - unsigned int level; + Level level; prevWith = 0; for (curEnv = &env, level = 1; curEnv; curEnv = curEnv->up, level++) if (curEnv->isWith) { diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index fe12890e9..13256272c 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -133,6 +133,9 @@ struct ExprPath : Expr Value * maybeThunk(EvalState & state, Env & env); }; +typedef uint32_t Level; +typedef uint32_t Displacement; + struct ExprVar : Expr { Pos pos; @@ -148,8 +151,8 @@ struct ExprVar : Expr value is obtained by getting the attribute named `name' from the set stored in the environment that is `level' levels up from the current one.*/ - unsigned int level; - unsigned int displ; + Level level; + Displacement displ; ExprVar(const Symbol & name) : name(name) { }; ExprVar(const Pos & pos, const Symbol & name) : pos(pos), name(name) { }; @@ -183,7 +186,7 @@ struct ExprAttrs : Expr bool inherited; Expr * e; Pos pos; - unsigned int displ; // displacement + Displacement displ; // displacement AttrDef(Expr * e, const Pos & pos, bool inherited=false) : inherited(inherited), e(e), pos(pos) { }; AttrDef() { }; @@ -352,7 +355,7 @@ struct StaticEnv const StaticEnv * up; // Note: these must be in sorted order. - typedef std::vector> Vars; + typedef std::vector> Vars; Vars vars; StaticEnv(bool isWith, const StaticEnv * up, size_t expectedSize = 0) : isWith(isWith), up(up) { -- cgit v1.2.3 From cbfbf71e0821fd79ae4837e7fe03c051437f43dd Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 2 Mar 2020 17:22:31 +0100 Subject: Use callFunction() with an array for some calls with arity > 1 --- src/libexpr/primops.cc | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 8a573f35c..9b166d63c 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1883,9 +1883,6 @@ static void addPath( Value arg1; mkString(arg1, path); - Value fun2; - state.callFunction(*filterFun, arg1, fun2, noPos); - Value arg2; mkString(arg2, S_ISREG(st.st_mode) ? "regular" : @@ -1893,8 +1890,9 @@ static void addPath( S_ISLNK(st.st_mode) ? "symlink" : "unknown" /* not supported, will fail! */); + Value * args []{&arg1, &arg2}; Value res; - state.callFunction(fun2, arg2, res, noPos); + state.callFunction(*filterFun, 2, args, res, pos); return state.forceBool(res, pos); }) : defaultPathFilter; @@ -2695,10 +2693,9 @@ static void prim_foldlStrict(EvalState & state, const Pos & pos, Value * * args, Value * vCur = args[1]; for (unsigned int n = 0; n < args[2]->listSize(); ++n) { - Value vTmp; - state.callFunction(*args[0], *vCur, vTmp, pos); + Value * vs []{vCur, args[2]->listElems()[n]}; vCur = n == args[2]->listSize() - 1 ? &v : state.allocValue(); - state.callFunction(vTmp, *args[2]->listElems()[n], *vCur, pos); + state.callFunction(*args[0], 2, vs, *vCur, pos); } state.forceValue(v, pos); } else { @@ -2819,17 +2816,16 @@ static void prim_sort(EvalState & state, const Pos & pos, Value * * args, Value v.listElems()[n] = args[1]->listElems()[n]; } - auto comparator = [&](Value * a, Value * b) { /* Optimization: if the comparator is lessThan, bypass callFunction. */ if (args[0]->isPrimOp() && args[0]->primOp->fun == prim_lessThan) return CompareValues()(a, b); - Value vTmp1, vTmp2; - state.callFunction(*args[0], *a, vTmp1, pos); - state.callFunction(vTmp1, *b, vTmp2, pos); - return state.forceBool(vTmp2, pos); + Value * vs[] = {a, b}; + Value vBool; + state.callFunction(*args[0], 2, vs, vBool, pos); + return state.forceBool(vBool, pos); }; /* FIXME: std::sort can segfault if the comparator is not a strict -- cgit v1.2.3 From acd6bddec706e2f180279ca79a0c1a4abac416d6 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 2 Mar 2020 18:15:06 +0100 Subject: Fix derivation primop --- src/libexpr/eval.cc | 12 +++++++++--- src/libexpr/eval.hh | 2 ++ src/libexpr/primops.cc | 13 ++++++++----- 3 files changed, 19 insertions(+), 8 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 641687f2a..d371a3fb8 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -583,11 +583,17 @@ Value * EvalState::addConstant(const string & name, Value & v) { Value * v2 = allocValue(); *v2 = v; + addConstant(name, v2); + return v2; +} + + +void EvalState::addConstant(const string & name, Value * v) +{ staticBaseEnv.vars.emplace_back(symbols.create(name), baseEnvDispl); - baseEnv.values[baseEnvDispl++] = v2; + baseEnv.values[baseEnvDispl++] = v; string name2 = string(name, 0, 2) == "__" ? string(name, 2) : name; - baseEnv.values[0]->attrs->push_back(Attr(symbols.create(name2), v2)); - return v2; + baseEnv.values[0]->attrs->push_back(Attr(symbols.create(name2), v)); } diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index f87dcdd8e..65cf04b57 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -277,6 +277,8 @@ private: Value * addConstant(const string & name, Value & v); + void addConstant(const string & name, Value * v); + constexpr static size_t maxPrimOpArity = 3; Value * addPrimOp(const string & name, diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 9b166d63c..e4107dbe1 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -3725,18 +3725,21 @@ void EvalState::createBaseEnv() /* Add a wrapper around the derivation primop that computes the `drvPath' and `outPath' attributes lazily. */ - staticBaseEnv.sort(); sDerivationNix = symbols.create("//builtin/derivation.nix"); - eval(parse( - #include "primops/derivation.nix.gen.hh" - , foFile, sDerivationNix, "/", staticBaseEnv), v); - addConstant("derivation", v); + auto vDerivation = allocValue(); + addConstant("derivation", vDerivation); /* Now that we've added all primops, sort the `builtins' set, because attribute lookups expect it to be sorted. */ baseEnv.values[0]->attrs->sort(); staticBaseEnv.sort(); + + /* Note: we have to initialize the 'derivation' constant *after* + building baseEnv/staticBaseEnv because it uses 'builtins'. */ + eval(parse( + #include "primops/derivation.nix.gen.hh" + , foFile, sDerivationNix, "/", staticBaseEnv), *vDerivation); } -- cgit v1.2.3 From 40925337a972991468335b90ce4f685f7102d830 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 2 Mar 2020 22:41:56 +0100 Subject: Remove maxPrimOpArity --- src/libexpr/eval.cc | 2 -- src/libexpr/eval.hh | 2 -- 2 files changed, 4 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index d371a3fb8..402de78ad 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -600,8 +600,6 @@ void EvalState::addConstant(const string & name, Value * v) Value * EvalState::addPrimOp(const string & name, size_t arity, PrimOpFun primOp) { - assert(arity <= maxPrimOpArity); - auto name2 = string(name, 0, 2) == "__" ? string(name, 2) : name; Symbol sym = symbols.create(name2); diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 65cf04b57..1aab8e166 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -279,8 +279,6 @@ private: void addConstant(const string & name, Value * v); - constexpr static size_t maxPrimOpArity = 3; - Value * addPrimOp(const string & name, size_t arity, PrimOpFun primOp); -- cgit v1.2.3 From 1f3c3a3785b21fe349480b562d5f97b8cc9caa6d Mon Sep 17 00:00:00 2001 From: regnat Date: Fri, 5 Nov 2021 16:17:49 +0100 Subject: Make the flake options work when using the daemon MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When setting flake-local options (with the `nixConfig` field), forward these options to the daemon in case we’re using one. This is necessary in particular for options like `binary-caches` or `post-build-hook` to make sense. Fix --- src/libexpr/flake/flake.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/libexpr') diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc index c9d848495..07ed3caa2 100644 --- a/src/libexpr/flake/flake.cc +++ b/src/libexpr/flake/flake.cc @@ -307,7 +307,7 @@ LockedFlake lockFlake( if (lockFlags.applyNixConfig) { flake.config.apply(); - // FIXME: send new config to the daemon. + state.store->setOptions(); } try { -- cgit v1.2.3 From 9d4dcff37a14c5bab98761e417a2335a9ac68c96 Mon Sep 17 00:00:00 2001 From: Alexander Bantyev Date: Sat, 23 Oct 2021 21:31:46 +0300 Subject: addPath: allow paths with references Since 4806f2f6b0fd2cae401b89fe19d8c528ffd88b5f, we can't have paths with references passed to builtins.{path,filterSource}. This prevents many cases of those functions called on IFD outputs from working. Resolve this by passing the references found in the original path to the added path. --- src/libexpr/primops.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 4e0eda7f3..4a615c59d 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1859,12 +1859,12 @@ static void addPath( // be rewritten to the actual output). state.realiseContext(context); + StorePathSet refs = StorePathSet(); + if (state.store->isInStore(path)) { auto [storePath, subPath] = state.store->toStorePath(path); - auto info = state.store->queryPathInfo(storePath); - if (!info->references.empty()) - throw EvalError("store path '%s' is not allowed to have references", - state.store->printStorePath(storePath)); + // FIXME: we should scanForReferences on the path before adding it + refs = state.store->queryPathInfo(storePath)->references; path = state.store->toRealPath(storePath) + subPath; } @@ -1904,7 +1904,7 @@ static void addPath( if (!expectedHash || !state.store->isValidPath(*expectedStorePath)) { dstPath = state.store->printStorePath(settings.readOnlyMode ? state.store->computeStorePathForPath(name, path, method, htSHA256, filter).first - : state.store->addToStore(name, path, method, htSHA256, filter, state.repair)); + : state.store->addToStore(name, path, method, htSHA256, filter, state.repair, refs)); if (expectedHash && expectedStorePath != state.store->parseStorePath(dstPath)) throw Error("store path mismatch in (possibly filtered) path added from '%s'", path); } else -- cgit v1.2.3 From 8e7359db6496ee72a84322d46afa322604afd1a9 Mon Sep 17 00:00:00 2001 From: Andreas Rammhold Date: Sun, 7 Nov 2021 18:21:24 +0100 Subject: Remove unused "" symbol The requirement for the symbol has been removed since at least 7d47498. --- src/libexpr/parser.y | 2 -- 1 file changed, 2 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 2e8a04143..923997bf6 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -33,11 +33,9 @@ namespace nix { Symbol file; FileOrigin origin; std::optional error; - Symbol sLetBody; ParseData(EvalState & state) : state(state) , symbols(state.symbols) - , sLetBody(symbols.create("")) { }; }; -- cgit v1.2.3 From 0b005bc9d67b3f621bc78e5ecb2cd834172d5563 Mon Sep 17 00:00:00 2001 From: Alexander Bantyev Date: Tue, 9 Nov 2021 12:24:49 +0300 Subject: addToStore, addToStoreFromDump: refactor: pass refs by const reference Co-Authored-By: Eelco Dolstra --- src/libexpr/primops.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/libexpr') diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 4a615c59d..047856abc 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1859,7 +1859,7 @@ static void addPath( // be rewritten to the actual output). state.realiseContext(context); - StorePathSet refs = StorePathSet(); + StorePathSet refs; if (state.store->isInStore(path)) { auto [storePath, subPath] = state.store->toStorePath(path); -- cgit v1.2.3 From 07bffe799853ef5e2757b9892e4e3dac89bfccbb Mon Sep 17 00:00:00 2001 From: Alexander Bantyev Date: Wed, 10 Nov 2021 14:56:22 +0300 Subject: Flakes: refetch the input when a follows disappears When an input follows disappears, we can't just reuse the old lock file entries since we may be missing some required ones. Refetch the input when this happens. Closes https://github.com/NixOS/nix/issues/5289 --- src/libexpr/flake/flake.cc | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'src/libexpr') diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc index 07ed3caa2..1dc6f5694 100644 --- a/src/libexpr/flake/flake.cc +++ b/src/libexpr/flake/flake.cc @@ -462,6 +462,8 @@ LockedFlake lockFlake( those. */ FlakeInputs fakeInputs; + bool refetch = false; + for (auto & i : oldLock->inputs) { if (auto lockedNode = std::get_if<0>(&i.second)) { fakeInputs.emplace(i.first, FlakeInput { @@ -469,12 +471,24 @@ LockedFlake lockFlake( .isFlake = (*lockedNode)->isFlake, }); } else if (auto follows = std::get_if<1>(&i.second)) { + auto o = input.overrides.find(i.first); + // If the override disappeared, we have to refetch the flake, + // since some of the inputs may not be present in the lockfile. + if (o == input.overrides.end()) { + refetch = true; + // There's no point populating the rest of the fake inputs, + // since we'll refetch the flake anyways. + break; + } fakeInputs.emplace(i.first, FlakeInput { .follows = *follows, }); } } + if (refetch) + fakeInputs = getFlake(state, oldLock->lockedRef, false, flakeCache).inputs; + computeLocks(fakeInputs, childNode, inputPath, oldLock, parent, parentPath); } -- cgit v1.2.3 From 30496af5980fd03706f587eef014e630e9d9d318 Mon Sep 17 00:00:00 2001 From: Tom Bereknyei Date: Fri, 12 Nov 2021 09:50:07 -0500 Subject: Adds an accept-flake-config flag --- src/libexpr/flake/config.cc | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/flake/config.cc b/src/libexpr/flake/config.cc index 41b6f78ed..c03f4106c 100644 --- a/src/libexpr/flake/config.cc +++ b/src/libexpr/flake/config.cc @@ -1,4 +1,5 @@ #include "flake.hh" +#include "globals.hh" #include @@ -52,21 +53,19 @@ void ConfigFile::apply() auto trustedList = readTrustedList(); bool trusted = false; - - if (auto saved = get(get(trustedList, name).value_or(std::map()), valueS)) { + if (nix::settings.acceptFlakeConfig){ + trusted = true; + } else if (auto saved = get(get(trustedList, name).value_or(std::map()), valueS)) { trusted = *saved; + warn("Using saved setting for '%s = %s' from ~/.local/share/nix/trusted-settings.json.", name,valueS); } else { // FIXME: filter ANSI escapes, newlines, \r, etc. - if (std::tolower(logger->ask(fmt("do you want to allow configuration setting '%s' to be set to '" ANSI_RED "%s" ANSI_NORMAL "' (y/N)?", name, valueS)).value_or('n')) != 'y') { - if (std::tolower(logger->ask("do you want to permanently mark this value as untrusted (y/N)?").value_or('n')) == 'y') { - trustedList[name][valueS] = false; - writeTrustedList(trustedList); - } - } else { - if (std::tolower(logger->ask("do you want to permanently mark this value as trusted (y/N)?").value_or('n')) == 'y') { - trustedList[name][valueS] = trusted = true; - writeTrustedList(trustedList); - } + if (std::tolower(logger->ask(fmt("do you want to allow configuration setting '%s' to be set to '" ANSI_RED "%s" ANSI_NORMAL "' (y/N)?", name, valueS)).value_or('n')) == 'y') { + trusted = true; + } + if (std::tolower(logger->ask(fmt("do you want to permanently mark this value as %s (y/N)?", trusted ? "trusted": "untrusted" )).value_or('n')) == 'y') { + trustedList[name][valueS] = trusted; + writeTrustedList(trustedList); } } -- cgit v1.2.3 From d0e9e184895b62d193a6eff5e332a338d3664e5b Mon Sep 17 00:00:00 2001 From: Kevin Amado Date: Sat, 13 Nov 2021 20:29:31 -0500 Subject: toXML: display errors position - This change applies to builtins.toXML and inner workings - Proof of concept: ```nix let e = builtins.toXML e; in e ``` - Before: ``` $ nix-instantiate --eval poc.nix error: infinite recursion encountered ``` - After: ``` $ nix-instantiate --eval poc.nix error: infinite recursion encountered at /data/github/kamadorueda/nix/poc.nix:1:9: 1| let e = builtins.toXML e; in e | ``` --- src/libexpr/primops.cc | 2 +- src/libexpr/value-to-xml.cc | 25 ++++++++++++++----------- src/libexpr/value-to-xml.hh | 4 ++-- src/libexpr/value.hh | 3 ++- 4 files changed, 19 insertions(+), 15 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index aaeafb931..668ad00ec 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1582,7 +1582,7 @@ static void prim_toXML(EvalState & state, const Pos & pos, Value * * args, Value { std::ostringstream out; PathSet context; - printValueAsXML(state, true, false, *args[0], out, context); + printValueAsXML(state, true, false, *args[0], out, context, pos); mkString(v, out.str(), context); } diff --git a/src/libexpr/value-to-xml.cc b/src/libexpr/value-to-xml.cc index b44455f5f..54268ece0 100644 --- a/src/libexpr/value-to-xml.cc +++ b/src/libexpr/value-to-xml.cc @@ -18,7 +18,8 @@ static XMLAttrs singletonAttrs(const string & name, const string & value) static void printValueAsXML(EvalState & state, bool strict, bool location, - Value & v, XMLWriter & doc, PathSet & context, PathSet & drvsSeen); + Value & v, XMLWriter & doc, PathSet & context, PathSet & drvsSeen, + const Pos & pos); static void posToXML(XMLAttrs & xmlAttrs, const Pos & pos) @@ -46,17 +47,18 @@ static void showAttrs(EvalState & state, bool strict, bool location, XMLOpenElement _(doc, "attr", xmlAttrs); printValueAsXML(state, strict, location, - *a.value, doc, context, drvsSeen); + *a.value, doc, context, drvsSeen, *a.pos); } } static void printValueAsXML(EvalState & state, bool strict, bool location, - Value & v, XMLWriter & doc, PathSet & context, PathSet & drvsSeen) + Value & v, XMLWriter & doc, PathSet & context, PathSet & drvsSeen, + const Pos & pos) { checkInterrupt(); - if (strict) state.forceValue(v); + if (strict) state.forceValue(v, pos); switch (v.type()) { @@ -91,14 +93,14 @@ static void printValueAsXML(EvalState & state, bool strict, bool location, Path drvPath; a = v.attrs->find(state.sDrvPath); if (a != v.attrs->end()) { - if (strict) state.forceValue(*a->value); + if (strict) state.forceValue(*a->value, *a->pos); if (a->value->type() == nString) xmlAttrs["drvPath"] = drvPath = a->value->string.s; } a = v.attrs->find(state.sOutPath); if (a != v.attrs->end()) { - if (strict) state.forceValue(*a->value); + if (strict) state.forceValue(*a->value, *a->pos); if (a->value->type() == nString) xmlAttrs["outPath"] = a->value->string.s; } @@ -121,7 +123,7 @@ static void printValueAsXML(EvalState & state, bool strict, bool location, case nList: { XMLOpenElement _(doc, "list"); for (unsigned int n = 0; n < v.listSize(); ++n) - printValueAsXML(state, strict, location, *v.listElems()[n], doc, context, drvsSeen); + printValueAsXML(state, strict, location, *v.listElems()[n], doc, context, drvsSeen, pos); break; } @@ -149,7 +151,7 @@ static void printValueAsXML(EvalState & state, bool strict, bool location, } case nExternal: - v.external->printValueAsXML(state, strict, location, doc, context, drvsSeen); + v.external->printValueAsXML(state, strict, location, doc, context, drvsSeen, pos); break; case nFloat: @@ -163,19 +165,20 @@ static void printValueAsXML(EvalState & state, bool strict, bool location, void ExternalValueBase::printValueAsXML(EvalState & state, bool strict, - bool location, XMLWriter & doc, PathSet & context, PathSet & drvsSeen) const + bool location, XMLWriter & doc, PathSet & context, PathSet & drvsSeen, + const Pos & pos) const { doc.writeEmptyElement("unevaluated"); } void printValueAsXML(EvalState & state, bool strict, bool location, - Value & v, std::ostream & out, PathSet & context) + Value & v, std::ostream & out, PathSet & context, const Pos & pos) { XMLWriter doc(true, out); XMLOpenElement root(doc, "expr"); PathSet drvsSeen; - printValueAsXML(state, strict, location, v, doc, context, drvsSeen); + printValueAsXML(state, strict, location, v, doc, context, drvsSeen, pos); } diff --git a/src/libexpr/value-to-xml.hh b/src/libexpr/value-to-xml.hh index 97657327e..cc778a2cb 100644 --- a/src/libexpr/value-to-xml.hh +++ b/src/libexpr/value-to-xml.hh @@ -9,6 +9,6 @@ namespace nix { void printValueAsXML(EvalState & state, bool strict, bool location, - Value & v, std::ostream & out, PathSet & context); - + Value & v, std::ostream & out, PathSet & context, const Pos & pos); + } diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh index a1f131f9e..3bb97b3c2 100644 --- a/src/libexpr/value.hh +++ b/src/libexpr/value.hh @@ -94,7 +94,8 @@ class ExternalValueBase /* Print the value as XML. Defaults to unevaluated */ virtual void printValueAsXML(EvalState & state, bool strict, bool location, - XMLWriter & doc, PathSet & context, PathSet & drvsSeen) const; + XMLWriter & doc, PathSet & context, PathSet & drvsSeen, + const Pos & pos) const; virtual ~ExternalValueBase() { -- cgit v1.2.3 From 671817a858875045b5f99a15ac92addaf9b207f5 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 15 Nov 2021 18:44:27 +0100 Subject: Simplify lockFlake() a bit --- src/libexpr/flake/flake.cc | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc index 1dc6f5694..f5be67d67 100644 --- a/src/libexpr/flake/flake.cc +++ b/src/libexpr/flake/flake.cc @@ -446,24 +446,18 @@ LockedFlake lockFlake( update it. */ auto lb = lockFlags.inputUpdates.lower_bound(inputPath); - auto hasChildUpdate = + auto mustRefetch = lb != lockFlags.inputUpdates.end() && lb->size() > inputPath.size() && std::equal(inputPath.begin(), inputPath.end(), lb->begin()); - if (hasChildUpdate) { - auto inputFlake = getFlake( - state, oldLock->lockedRef, false, flakeCache); - computeLocks(inputFlake.inputs, childNode, inputPath, oldLock, parent, parentPath); - } else { + FlakeInputs fakeInputs; + + if (!mustRefetch) { /* No need to fetch this flake, we can be lazy. However there may be new overrides on the inputs of this flake, so we need to check those. */ - FlakeInputs fakeInputs; - - bool refetch = false; - for (auto & i : oldLock->inputs) { if (auto lockedNode = std::get_if<0>(&i.second)) { fakeInputs.emplace(i.first, FlakeInput { @@ -475,7 +469,7 @@ LockedFlake lockFlake( // If the override disappeared, we have to refetch the flake, // since some of the inputs may not be present in the lockfile. if (o == input.overrides.end()) { - refetch = true; + mustRefetch = true; // There's no point populating the rest of the fake inputs, // since we'll refetch the flake anyways. break; @@ -485,13 +479,14 @@ LockedFlake lockFlake( }); } } - - if (refetch) - fakeInputs = getFlake(state, oldLock->lockedRef, false, flakeCache).inputs; - - computeLocks(fakeInputs, childNode, inputPath, oldLock, parent, parentPath); } + computeLocks( + mustRefetch + ? getFlake(state, oldLock->lockedRef, false, flakeCache).inputs + : fakeInputs, + childNode, inputPath, oldLock, parent, parentPath); + } else { /* We need to create a new lock file entry. So fetch this input. */ -- cgit v1.2.3 From e41cf8511f68e7e67786d8735d03c961c34cb2ad Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 16 Nov 2021 17:44:19 +0100 Subject: Don't hang when calling an attrset Fixes #5565. --- src/libexpr/eval.cc | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 402de78ad..615f020e4 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -1275,6 +1275,8 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & } }; + Attr * functor; + while (nrArgs > 0) { if (vCur.isLambda()) { @@ -1403,16 +1405,14 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & } } - else if (vCur.type() == nAttrs) { - if (auto functor = vCur.attrs->get(sFunctor)) { - /* 'vCur" may be allocated on the stack of the calling - function, but for functors we may keep a reference, - so heap-allocate a copy and use that instead. */ - Value * args2[] = {allocValue()}; - *args2[0] = vCur; - /* !!! Should we use the attr pos here? */ - callFunction(*functor->value, 1, args2, vCur, pos); - } + else if (vCur.type() == nAttrs && (functor = vCur.attrs->get(sFunctor))) { + /* 'vCur" may be allocated on the stack of the calling + function, but for functors we may keep a reference, so + heap-allocate a copy and use that instead. */ + Value * args2[] = {allocValue()}; + *args2[0] = vCur; + /* !!! Should we use the attr pos here? */ + callFunction(*functor->value, 1, args2, vCur, pos); } else -- cgit v1.2.3 From d7bae52b9d418e208593c6641bce54f6a82458f4 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 16 Nov 2021 22:34:17 +0100 Subject: Call functors with both arguments at once This is not really useful on its own, but it does recover the 'infinite recursion' error message for '{ __functor = x: x; } 1', and is more efficient in conjunction with #3718. Fixes #5515. --- src/libexpr/eval.cc | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 615f020e4..f1ff3a6e0 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -1406,13 +1406,15 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & } else if (vCur.type() == nAttrs && (functor = vCur.attrs->get(sFunctor))) { - /* 'vCur" may be allocated on the stack of the calling + /* 'vCur' may be allocated on the stack of the calling function, but for functors we may keep a reference, so heap-allocate a copy and use that instead. */ - Value * args2[] = {allocValue()}; + Value * args2[] = {allocValue(), args[0]}; *args2[0] = vCur; /* !!! Should we use the attr pos here? */ - callFunction(*functor->value, 1, args2, vCur, pos); + callFunction(*functor->value, 2, args2, vCur, pos); + nrArgs--; + args++; } else -- cgit v1.2.3 From 6d46b5b609f8f85968e1ca7aaf7c57dd52d0521c Mon Sep 17 00:00:00 2001 From: Kalle Jepsen Date: Wed, 17 Nov 2021 08:41:26 +0100 Subject: Fix detection of scp-style URIs to support non-standard SSH ports for git --- src/libexpr/primops/fetchTree.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/libexpr') diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index e6becdafc..b307ac04a 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -74,8 +74,11 @@ std::string fixURI(std::string uri, EvalState & state, const std::string & defau std::string fixURIForGit(std::string uri, EvalState & state) { + /* Detects scp-style uris (e.g. git@github.com:NixOS/nix) and fixes + * them by removing the `:` and assuming a scheme of `ssh://` + * */ static std::regex scp_uri("([^/].*)@(.*):(.*)"); - if (uri[0] != '/' && std::regex_match(uri, scp_uri)) + if (uri[0] != '/' && std::regex_match(uri, scp_uri) && uri.find("://") == std::string::npos) return fixURI(std::regex_replace(uri, scp_uri, "$1@$2/$3"), state, "ssh"); else return fixURI(uri, state); -- cgit v1.2.3 From d03e89e5d19a7082cc1e8e5ff10d7f62a0a6e937 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 3 Mar 2020 15:32:20 +0100 Subject: Parse '(f x) y' the same as 'f x y' (cherry picked from commit 5253cb4b68ad248f37b27849c0ebf3614e4f2777) --- src/libexpr/parser.y | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 923997bf6..c1f4e72e0 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -39,12 +39,6 @@ namespace nix { { }; }; - // Helper to prevent an expensive dynamic_cast call in expr_app. - struct App - { - Expr * e; - bool isCall; - }; } #define YY_DECL int yylex \ @@ -284,12 +278,10 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err char * uri; std::vector * attrNames; std::vector * string_parts; - nix::App app; // bool == whether this is an ExprCall } %type start expr expr_function expr_if expr_op -%type expr_select expr_simple -%type expr_app +%type expr_select expr_simple expr_app %type expr_list %type binds %type formals @@ -377,20 +369,18 @@ expr_op | expr_op '*' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__mul")), {$1, $3}); } | expr_op '/' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__div")), {$1, $3}); } | expr_op CONCAT expr_op { $$ = new ExprOpConcatLists(CUR_POS, $1, $3); } - | expr_app { $$ = $1.e; } + | expr_app ; expr_app : expr_app expr_select { - if ($1.isCall) { - ((ExprCall *) $1.e)->args.push_back($2); + if (auto e2 = dynamic_cast($1)) { + e2->args.push_back($2); $$ = $1; - } else { - $$.e = new ExprCall(CUR_POS, $1.e, {$2}); - $$.isCall = true; - } + } else + $$ = new ExprCall(CUR_POS, $1, {$2}); } - | expr_select { $$.e = $1; $$.isCall = false; } + | expr_select ; expr_select -- cgit v1.2.3 From 46d2a5a10be7e48679a29d487adbb6f1d6fd452a Mon Sep 17 00:00:00 2001 From: Kalle Jepsen Date: Wed, 17 Nov 2021 13:49:10 +0100 Subject: Simplify fix by disallowing / in front of @ to match scp style --- src/libexpr/primops/fetchTree.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index b307ac04a..079513873 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -77,8 +77,8 @@ std::string fixURIForGit(std::string uri, EvalState & state) /* Detects scp-style uris (e.g. git@github.com:NixOS/nix) and fixes * them by removing the `:` and assuming a scheme of `ssh://` * */ - static std::regex scp_uri("([^/].*)@(.*):(.*)"); - if (uri[0] != '/' && std::regex_match(uri, scp_uri) && uri.find("://") == std::string::npos) + static std::regex scp_uri("([^/]*)@(.*):(.*)"); + if (uri[0] != '/' && std::regex_match(uri, scp_uri)) return fixURI(std::regex_replace(uri, scp_uri, "$1@$2/$3"), state, "ssh"); else return fixURI(uri, state); -- cgit v1.2.3 From 4318ba2ec568a9fe7d5b4b014df6b7d252ae3481 Mon Sep 17 00:00:00 2001 From: Tom Bereknyei Date: Fri, 19 Nov 2021 23:52:52 -0500 Subject: add real path to allowedPaths --- src/libexpr/eval.cc | 4 ++-- src/libexpr/primops.cc | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index f1ff3a6e0..97fc04711 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -520,7 +520,7 @@ Path EvalState::checkSourcePath(const Path & path_) } if (!found) - throw RestrictedPathError("access to path '%1%' is forbidden in restricted mode", abspath); + throw RestrictedPathError("access to absolute path '%1%' is forbidden in restricted mode", abspath); /* Resolve symlinks. */ debug(format("checking access to '%s'") % abspath); @@ -533,7 +533,7 @@ Path EvalState::checkSourcePath(const Path & path_) } } - throw RestrictedPathError("access to path '%1%' is forbidden in restricted mode", path); + throw RestrictedPathError("access to canonical path '%1%' is forbidden in restricted mode", path); } diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 5bd4e5545..8dccf6401 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -70,7 +70,7 @@ void EvalState::realiseContext(const PathSet & context) if (outputPaths.count(outputName) == 0) throw Error("derivation '%s' does not have an output named '%s'", store->printStorePath(drvPath), outputName); - allowedPaths->insert(store->printStorePath(outputPaths.at(outputName))); + allowPath(outputPaths.at(outputName)); } } } -- cgit v1.2.3 From 0768c08d999593fc23675b17c6f0480a35206c47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roman=20Fro=C5=82ow?= Date: Mon, 22 Nov 2021 13:35:35 +0100 Subject: Typo: change to normal singlequote --- src/libexpr/primops.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 5bd4e5545..09007a22c 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -2710,9 +2710,9 @@ static RegisterPrimOp primop_foldlStrict({ .args = {"op", "nul", "list"}, .doc = R"( Reduce a list by applying a binary operator, from left to right, - e.g. `foldl’ op nul [x0 x1 x2 ...] = op (op (op nul x0) x1) x2) + e.g. `foldl' op nul [x0 x1 x2 ...] = op (op (op nul x0) x1) x2) ...`. The operator is applied strictly, i.e., its arguments are - evaluated first. For example, `foldl’ (x: y: x + y) 0 [1 2 3]` + evaluated first. For example, `foldl' (x: y: x + y) 0 [1 2 3]` evaluates to 6. )", .fun = prim_foldlStrict, -- cgit v1.2.3 From 09471d2680292af48b2788108de56a8da755d661 Mon Sep 17 00:00:00 2001 From: Silvan Mosberger Date: Mon, 22 Nov 2021 23:56:40 +0100 Subject: Make lists be comparable Makes lists comparable using lexicographic comparison. Increments builtins.langVersion in order for this change to be detectable --- src/libexpr/primops.cc | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 8dccf6401..69f4737a8 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -517,7 +517,11 @@ static RegisterPrimOp primop_isPath({ struct CompareValues { - bool operator () (const Value * v1, const Value * v2) const + EvalState & state; + + CompareValues(EvalState & state) : state(state) { }; + + bool operator () (Value * v1, Value * v2) const { if (v1->type() == nFloat && v2->type() == nInt) return v1->fpoint < v2->integer; @@ -534,6 +538,17 @@ struct CompareValues return strcmp(v1->string.s, v2->string.s) < 0; case nPath: return strcmp(v1->path, v2->path) < 0; + case nList: + // Lexicographic comparison + for (size_t i = 0;; i++) { + if (i == v2->listSize()) { + return false; + } else if (i == v1->listSize()) { + return true; + } else if (!state.eqValues(*v1->listElems()[i], *v2->listElems()[i])) { + return (*this)(v1->listElems()[i], v2->listElems()[i]); + } + } default: throw EvalError("cannot compare %1% with %2%", showType(*v1), showType(*v2)); } @@ -621,7 +636,8 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar ValueList res; // `doneKeys' doesn't need to be a GC root, because its values are // reachable from res. - set doneKeys; + auto cmp = CompareValues(state); + set doneKeys(cmp); while (!workSet.empty()) { Value * e = *(workSet.begin()); workSet.pop_front(); @@ -2821,7 +2837,7 @@ static void prim_sort(EvalState & state, const Pos & pos, Value * * args, Value /* Optimization: if the comparator is lessThan, bypass callFunction. */ if (args[0]->isPrimOp() && args[0]->primOp->fun == prim_lessThan) - return CompareValues()(a, b); + return CompareValues(state)(a, b); Value * vs[] = {a, b}; Value vBool; @@ -3103,7 +3119,7 @@ static void prim_lessThan(EvalState & state, const Pos & pos, Value * * args, Va { state.forceValue(*args[0], pos); state.forceValue(*args[1], pos); - CompareValues comp; + CompareValues comp{state}; mkBool(v, comp(args[0], args[1])); } @@ -3693,7 +3709,7 @@ void EvalState::createBaseEnv() language feature gets added. It's not necessary to increase it when primops get added, because you can just use `builtins ? primOp' to check. */ - mkInt(v, 5); + mkInt(v, 6); addConstant("__langVersion", v); // Miscellaneous -- cgit v1.2.3 From c47027f3a139669dfb607c22b153564ff53d111c Mon Sep 17 00:00:00 2001 From: regnat Date: Thu, 25 Nov 2021 14:48:01 +0100 Subject: Fix the error when accessing a forbidden path in pure eval MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If we’re in pure eval mode, then tell that in the error message rather than (wrongly) speaking about restricted mode. Fix https://github.com/NixOS/nix/issues/5611 --- src/libexpr/eval.cc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 97fc04711..1fd609bd4 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -519,8 +519,12 @@ Path EvalState::checkSourcePath(const Path & path_) } } - if (!found) - throw RestrictedPathError("access to absolute path '%1%' is forbidden in restricted mode", abspath); + if (!found) { + auto modeInformation = evalSettings.pureEval + ? "in pure eval mode (use '--impure' to override)" + : "in restricted mode"; + throw RestrictedPathError("access to absolute path '%1%' is forbidden %2%", abspath, modeInformation); + } /* Resolve symlinks. */ debug(format("checking access to '%s'") % abspath); -- cgit v1.2.3 From b6c8e57056f81fa3c2827a7fdc6f335ec54727bd Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 24 Nov 2021 20:21:34 +0100 Subject: Support range-based for loop over list values --- src/libexpr/eval.cc | 18 +++++----- src/libexpr/flake/flake.cc | 3 +- src/libexpr/get-drvs.cc | 22 ++++++------ src/libexpr/primops.cc | 82 ++++++++++++++++++++---------------------- src/libexpr/primops/context.cc | 7 ++-- src/libexpr/value-to-json.cc | 4 +-- src/libexpr/value-to-xml.cc | 4 +-- src/libexpr/value.hh | 30 ++++++++++++++++ 8 files changed, 97 insertions(+), 73 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 97fc04711..5adac87cd 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -119,8 +119,8 @@ void printValue(std::ostream & str, std::set & active, const Valu case tList2: case tListN: str << "[ "; - for (unsigned int n = 0; n < v.listSize(); ++n) { - printValue(str, active, *v.listElems()[n]); + for (auto v2 : v.listItems()) { + printValue(str, active, *v2); str << " "; } str << "]"; @@ -1151,8 +1151,8 @@ void ExprLet::eval(EvalState & state, Env & env, Value & v) void ExprList::eval(EvalState & state, Env & env, Value & v) { state.mkList(v, elems.size()); - for (size_t n = 0; n < elems.size(); ++n) - v.listElems()[n] = elems[n]->maybeThunk(state, env); + for (auto [n, v2] : enumerate(v.listItems())) + const_cast(v2) = elems[n]->maybeThunk(state, env); } @@ -1732,8 +1732,8 @@ void EvalState::forceValueDeep(Value & v) } else if (v.isList()) { - for (size_t n = 0; n < v.listSize(); ++n) - recurse(*v.listElems()[n]); + for (auto v2 : v.listItems()) + recurse(*v2); } }; @@ -1917,12 +1917,12 @@ string EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context, if (v.isList()) { string result; - for (size_t n = 0; n < v.listSize(); ++n) { - result += coerceToString(pos, *v.listElems()[n], + for (auto [n, v2] : enumerate(v.listItems())) { + result += coerceToString(pos, *v2, context, coerceMore, copyToStore); if (n < v.listSize() - 1 /* !!! not quite correct */ - && (!v.listElems()[n]->isList() || v.listElems()[n]->listSize() != 0)) + && (!v2->isList() || v2->listSize() != 0)) result += " "; } return result; diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc index f5be67d67..06136579e 100644 --- a/src/libexpr/flake/flake.cc +++ b/src/libexpr/flake/flake.cc @@ -257,8 +257,7 @@ static Flake getFlake( flake.config.settings.insert({setting.name, state.forceBool(*setting.value, *setting.pos)}); else if (setting.value->type() == nList) { std::vector ss; - for (unsigned int n = 0; n < setting.value->listSize(); ++n) { - auto elem = setting.value->listElems()[n]; + for (auto elem : setting.value->listItems()) { if (elem->type() != nString) throw TypeError("list element in flake configuration setting '%s' is %s while a string is expected", setting.name, showType(*setting.value)); diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc index f774e6493..ed4c47fbb 100644 --- a/src/libexpr/get-drvs.cc +++ b/src/libexpr/get-drvs.cc @@ -102,9 +102,9 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool onlyOutputsToInstall) state->forceList(*i->value, *i->pos); /* For each output... */ - for (unsigned int j = 0; j < i->value->listSize(); ++j) { + for (auto elem : i->value->listItems()) { /* Evaluate the corresponding set. */ - string name = state->forceStringNoCtx(*i->value->listElems()[j], *i->pos); + string name = state->forceStringNoCtx(*elem, *i->pos); Bindings::iterator out = attrs->find(state->symbols.create(name)); if (out == attrs->end()) continue; // FIXME: throw error? state->forceAttrs(*out->value); @@ -128,9 +128,9 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool onlyOutputsToInstall) /* ^ this shows during `nix-env -i` right under the bad derivation */ if (!outTI->isList()) throw errMsg; Outputs result; - for (auto i = outTI->listElems(); i != outTI->listElems() + outTI->listSize(); ++i) { - if ((*i)->type() != nString) throw errMsg; - auto out = outputs.find((*i)->string.s); + for (auto elem : outTI->listItems()) { + if (elem->type() != nString) throw errMsg; + auto out = outputs.find(elem->string.s); if (out == outputs.end()) throw errMsg; result.insert(*out); } @@ -174,8 +174,8 @@ bool DrvInfo::checkMeta(Value & v) { state->forceValue(v); if (v.type() == nList) { - for (unsigned int n = 0; n < v.listSize(); ++n) - if (!checkMeta(*v.listElems()[n])) return false; + for (auto elem : v.listItems()) + if (!checkMeta(*elem)) return false; return true; } else if (v.type() == nAttrs) { @@ -364,10 +364,10 @@ static void getDerivations(EvalState & state, Value & vIn, } else if (v.type() == nList) { - for (unsigned int n = 0; n < v.listSize(); ++n) { - string pathPrefix2 = addToPath(pathPrefix, (format("%1%") % n).str()); - if (getDerivation(state, *v.listElems()[n], pathPrefix2, drvs, done, ignoreAssertionFailures)) - getDerivations(state, *v.listElems()[n], pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures); + for (auto [n, elem] : enumerate(v.listItems())) { + string pathPrefix2 = addToPath(pathPrefix, fmt("%d", n)); + if (getDerivation(state, *elem, pathPrefix2, drvs, done, ignoreAssertionFailures)) + getDerivations(state, *elem, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures); } } diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 8cbeaa520..c0d59da8c 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -335,9 +335,8 @@ void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v) PathSet context; auto program = state.coerceToString(pos, *elems[0], context, false, false); Strings commandArgs; - for (unsigned int i = 1; i < args[0]->listSize(); ++i) { + for (unsigned int i = 1; i < args[0]->listSize(); ++i) commandArgs.emplace_back(state.coerceToString(pos, *elems[i], context, false, false)); - } try { state.realiseContext(context); } catch (InvalidPathError & e) { @@ -616,8 +615,8 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar state.forceList(*startSet->value, pos); ValueList workSet; - for (unsigned int n = 0; n < startSet->value->listSize(); ++n) - workSet.push_back(startSet->value->listElems()[n]); + for (auto elem : startSet->value->listItems()) + workSet.push_back(elem); /* Get the operator. */ Bindings::iterator op = getAttr( @@ -662,9 +661,9 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar state.forceList(call, pos); /* Add the values returned by the operator to the work set. */ - for (unsigned int n = 0; n < call.listSize(); ++n) { - state.forceValue(*call.listElems()[n], pos); - workSet.push_back(call.listElems()[n]); + for (auto elem : call.listItems()) { + state.forceValue(*elem, pos); + workSet.push_back(elem); } } @@ -1013,8 +1012,8 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * command-line arguments to the builder. */ else if (i->name == state.sArgs) { state.forceList(*i->value, pos); - for (unsigned int n = 0; n < i->value->listSize(); ++n) { - string s = state.coerceToString(posDrvName, *i->value->listElems()[n], context, true); + for (auto elem : i->value->listItems()) { + string s = state.coerceToString(posDrvName, *elem, context, true); drv.args.push_back(s); } } @@ -1044,8 +1043,8 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * /* Require ‘outputs’ to be a list of strings. */ state.forceList(*i->value, posDrvName); Strings ss; - for (unsigned int n = 0; n < i->value->listSize(); ++n) - ss.emplace_back(state.forceStringNoCtx(*i->value->listElems()[n], posDrvName)); + for (auto elem : i->value->listItems()) + ss.emplace_back(state.forceStringNoCtx(*elem, posDrvName)); handleOutputs(ss); } @@ -1460,20 +1459,19 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va SearchPath searchPath; - for (unsigned int n = 0; n < args[0]->listSize(); ++n) { - Value & v2(*args[0]->listElems()[n]); - state.forceAttrs(v2, pos); + for (auto v2 : args[0]->listItems()) { + state.forceAttrs(*v2, pos); string prefix; - Bindings::iterator i = v2.attrs->find(state.symbols.create("prefix")); - if (i != v2.attrs->end()) + Bindings::iterator i = v2->attrs->find(state.symbols.create("prefix")); + if (i != v2->attrs->end()) prefix = state.forceStringNoCtx(*i->value, pos); i = getAttr( state, "findFile", "path", - v2.attrs, + v2->attrs, pos ); @@ -2239,9 +2237,9 @@ static void prim_removeAttrs(EvalState & state, const Pos & pos, Value * * args, /* Get the attribute names to be removed. */ std::set names; - for (unsigned int i = 0; i < args[1]->listSize(); ++i) { - state.forceStringNoCtx(*args[1]->listElems()[i], pos); - names.insert(state.symbols.create(args[1]->listElems()[i]->string.s)); + for (auto elem : args[1]->listItems()) { + state.forceStringNoCtx(*elem, pos); + names.insert(state.symbols.create(elem->string.s)); } /* Copy all attributes not in that set. Note that we don't need @@ -2249,7 +2247,7 @@ static void prim_removeAttrs(EvalState & state, const Pos & pos, Value * * args, vector. */ state.mkAttrs(v, args[0]->attrs->size()); for (auto & i : *args[0]->attrs) { - if (names.find(i.name) == names.end()) + if (!names.count(i.name)) v.attrs->push_back(i); } } @@ -2283,15 +2281,14 @@ static void prim_listToAttrs(EvalState & state, const Pos & pos, Value * * args, std::set seen; - for (unsigned int i = 0; i < args[0]->listSize(); ++i) { - Value & v2(*args[0]->listElems()[i]); - state.forceAttrs(v2, pos); + for (auto v2 : args[0]->listItems()) { + state.forceAttrs(*v2, pos); Bindings::iterator j = getAttr( state, "listToAttrs", state.sName, - v2.attrs, + v2->attrs, pos ); @@ -2303,7 +2300,7 @@ static void prim_listToAttrs(EvalState & state, const Pos & pos, Value * * args, state, "listToAttrs", state.sValue, - v2.attrs, + v2->attrs, pos ); v.attrs->push_back(Attr(sym, j2->value, j2->pos)); @@ -2370,11 +2367,10 @@ static void prim_catAttrs(EvalState & state, const Pos & pos, Value * * args, Va Value * res[args[1]->listSize()]; unsigned int found = 0; - for (unsigned int n = 0; n < args[1]->listSize(); ++n) { - Value & v2(*args[1]->listElems()[n]); - state.forceAttrs(v2, pos); - Bindings::iterator i = v2.attrs->find(attrName); - if (i != v2.attrs->end()) + for (auto v2 : args[1]->listItems()) { + state.forceAttrs(*v2, pos); + Bindings::iterator i = v2->attrs->find(attrName); + if (i != v2->attrs->end()) res[found++] = i->value; } @@ -2649,8 +2645,8 @@ static void prim_elem(EvalState & state, const Pos & pos, Value * * args, Value { bool res = false; state.forceList(*args[1], pos); - for (unsigned int n = 0; n < args[1]->listSize(); ++n) - if (state.eqValues(*args[0], *args[1]->listElems()[n])) { + for (auto elem : args[1]->listItems()) + if (state.eqValues(*args[0], *elem)) { res = true; break; } @@ -2709,8 +2705,8 @@ static void prim_foldlStrict(EvalState & state, const Pos & pos, Value * * args, if (args[2]->listSize()) { Value * vCur = args[1]; - for (unsigned int n = 0; n < args[2]->listSize(); ++n) { - Value * vs []{vCur, args[2]->listElems()[n]}; + for (auto [n, elem] : enumerate(args[2]->listItems())) { + Value * vs []{vCur, elem}; vCur = n == args[2]->listSize() - 1 ? &v : state.allocValue(); state.callFunction(*args[0], 2, vs, *vCur, pos); } @@ -2740,8 +2736,8 @@ static void anyOrAll(bool any, EvalState & state, const Pos & pos, Value * * arg state.forceList(*args[1], pos); Value vTmp; - for (unsigned int n = 0; n < args[1]->listSize(); ++n) { - state.callFunction(*args[0], *args[1]->listElems()[n], vTmp, pos); + for (auto elem : args[1]->listItems()) { + state.callFunction(*args[0], *elem, vTmp, pos); bool res = state.forceBool(vTmp, pos); if (res == any) { mkBool(v, any); @@ -3470,9 +3466,9 @@ static void prim_concatStringsSep(EvalState & state, const Pos & pos, Value * * res.reserve((args[1]->listSize() + 32) * sep.size()); bool first = true; - for (unsigned int n = 0; n < args[1]->listSize(); ++n) { + for (auto elem : args[1]->listItems()) { if (first) first = false; else res += sep; - res += state.coerceToString(pos, *args[1]->listElems()[n], context); + res += state.coerceToString(pos, *elem, context); } mkString(v, res, context); @@ -3501,14 +3497,14 @@ static void prim_replaceStrings(EvalState & state, const Pos & pos, Value * * ar vector from; from.reserve(args[0]->listSize()); - for (unsigned int n = 0; n < args[0]->listSize(); ++n) - from.push_back(state.forceString(*args[0]->listElems()[n], pos)); + for (auto elem : args[0]->listItems()) + from.push_back(state.forceString(*elem, pos)); vector> to; to.reserve(args[1]->listSize()); - for (unsigned int n = 0; n < args[1]->listSize(); ++n) { + for (auto elem : args[1]->listItems()) { PathSet ctx; - auto s = state.forceString(*args[1]->listElems()[n], ctx, pos); + auto s = state.forceString(*elem, ctx, pos); to.push_back(std::make_pair(std::move(s), std::move(ctx))); } diff --git a/src/libexpr/primops/context.cc b/src/libexpr/primops/context.cc index 31cf812b4..20545afd0 100644 --- a/src/libexpr/primops/context.cc +++ b/src/libexpr/primops/context.cc @@ -118,9 +118,8 @@ static void prim_getContext(EvalState & state, const Pos & pos, Value * * args, auto & outputsVal = *state.allocAttr(infoVal, state.sOutputs); state.mkList(outputsVal, info.second.outputs.size()); size_t i = 0; - for (const auto & output : info.second.outputs) { + for (const auto & output : info.second.outputs) mkString(*(outputsVal.listElems()[i++] = state.allocValue()), output); - } } infoVal.attrs->sort(); } @@ -181,8 +180,8 @@ static void prim_appendContext(EvalState & state, const Pos & pos, Value * * arg .errPos = *i.pos }); } - for (unsigned int n = 0; n < iter->value->listSize(); ++n) { - auto name = state.forceStringNoCtx(*iter->value->listElems()[n], *iter->pos); + for (auto elem : iter->value->listItems()) { + auto name = state.forceStringNoCtx(*elem, *iter->pos); context.insert("!" + name + "!" + string(i.name)); } } diff --git a/src/libexpr/value-to-json.cc b/src/libexpr/value-to-json.cc index 4d642c720..517da4c01 100644 --- a/src/libexpr/value-to-json.cc +++ b/src/libexpr/value-to-json.cc @@ -63,9 +63,9 @@ void printValueAsJSON(EvalState & state, bool strict, case nList: { auto list(out.list()); - for (unsigned int n = 0; n < v.listSize(); ++n) { + for (auto elem : v.listItems()) { auto placeholder(list.placeholder()); - printValueAsJSON(state, strict, *v.listElems()[n], pos, placeholder, context); + printValueAsJSON(state, strict, *elem, pos, placeholder, context); } break; } diff --git a/src/libexpr/value-to-xml.cc b/src/libexpr/value-to-xml.cc index 54268ece0..a875f82d7 100644 --- a/src/libexpr/value-to-xml.cc +++ b/src/libexpr/value-to-xml.cc @@ -122,8 +122,8 @@ static void printValueAsXML(EvalState & state, bool strict, bool location, case nList: { XMLOpenElement _(doc, "list"); - for (unsigned int n = 0; n < v.listSize(); ++n) - printValueAsXML(state, strict, location, *v.listElems()[n], doc, context, drvsSeen, pos); + for (auto v2 : v.listItems()) + printValueAsXML(state, strict, location, *v2, doc, context, drvsSeen, pos); break; } diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh index 3bb97b3c2..4b43e47ae 100644 --- a/src/libexpr/value.hh +++ b/src/libexpr/value.hh @@ -1,5 +1,7 @@ #pragma once +#include + #include "symbol-table.hh" #if HAVE_BOEHMGC @@ -350,6 +352,34 @@ public: bool isTrivial() const; std::vector> getContext(); + + auto listItems() + { + struct ListIterable + { + typedef Value * const * iterator; + iterator _begin, _end; + iterator begin() const { return _begin; } + iterator end() const { return _end; } + }; + assert(isList()); + auto begin = listElems(); + return ListIterable { begin, begin + listSize() }; + } + + auto listItems() const + { + struct ConstListIterable + { + typedef const Value * const * iterator; + iterator _begin, _end; + iterator begin() const { return _begin; } + iterator end() const { return _end; } + }; + assert(isList()); + auto begin = listElems(); + return ConstListIterable { begin, begin + listSize() }; + } }; -- cgit v1.2.3 From 90d8178009381fe7ad6f0380127df2bc5a0d1dc9 Mon Sep 17 00:00:00 2001 From: Andreas Rammhold Date: Sat, 6 Nov 2021 19:50:27 +0100 Subject: Don't move the arguments of the primOp Moving arguments of the primOp into the registration structure makes it impossible to initialize a second EvalState with the correct primOp registration. It will end up registering all those "RegisterPrimOp"'s with an arity of zero on all but the 2nd instance of the EvalState. Not moving the memory will add a tiny bit of memory overhead during the eval since we need a copy of all the argument lists of all the primOp's. The overhead shouldn't be too bad as it is static (based on the amonut of registered operations) and only occurs once during the interpreter startup. --- src/libexpr/primops.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/libexpr') diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index c0d59da8c..d1f4d9009 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -3732,7 +3732,7 @@ void EvalState::createBaseEnv() .fun = primOp.fun, .arity = std::max(primOp.args.size(), primOp.arity), .name = symbols.create(primOp.name), - .args = std::move(primOp.args), + .args = primOp.args, .doc = primOp.doc, }); -- cgit v1.2.3 From bd628cf3da14b7d3fe0ba00932eb26d545b4b893 Mon Sep 17 00:00:00 2001 From: Yorick van Pelt Date: Mon, 22 Nov 2021 13:29:49 +0100 Subject: flakes: fix boolean and int nixConfig values Some type confusion was causing ints to be pointers, and bools to be ints. Fixes #5621 --- src/libexpr/flake/config.cc | 8 ++++---- src/libexpr/flake/flake.cc | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/flake/config.cc b/src/libexpr/flake/config.cc index c03f4106c..7ecd61816 100644 --- a/src/libexpr/flake/config.cc +++ b/src/libexpr/flake/config.cc @@ -38,11 +38,11 @@ void ConfigFile::apply() // FIXME: Move into libutil/config.cc. std::string valueS; - if (auto s = std::get_if(&value)) + if (auto* s = std::get_if(&value)) valueS = *s; - else if (auto n = std::get_if(&value)) - valueS = fmt("%d", n); - else if (auto b = std::get_if>(&value)) + else if (auto* n = std::get_if(&value)) + valueS = fmt("%d", *n); + else if (auto* b = std::get_if>(&value)) valueS = b->t ? "true" : "false"; else if (auto ss = std::get_if>(&value)) valueS = concatStringsSep(" ", *ss); // FIXME: evil diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc index f5be67d67..732198031 100644 --- a/src/libexpr/flake/flake.cc +++ b/src/libexpr/flake/flake.cc @@ -254,7 +254,7 @@ static Flake getFlake( else if (setting.value->type() == nInt) flake.config.settings.insert({setting.name, state.forceInt(*setting.value, *setting.pos)}); else if (setting.value->type() == nBool) - flake.config.settings.insert({setting.name, state.forceBool(*setting.value, *setting.pos)}); + flake.config.settings.insert({setting.name, Explicit { state.forceBool(*setting.value, *setting.pos) }}); else if (setting.value->type() == nList) { std::vector ss; for (unsigned int n = 0; n < setting.value->listSize(); ++n) { -- cgit v1.2.3 From 8cbf862e6f6e8250d59491fdb88dfc11a805a970 Mon Sep 17 00:00:00 2001 From: Alexander Bantyev Date: Thu, 2 Dec 2021 11:35:50 +0300 Subject: Flakes: computeLocks: pass correct LockParent when reusing oldLock Previously, when we were attempting to reuse the old lockfile information in the computeLocks function, we have passed the parent of the current input to the next computeLocks call. This was incorrect, since the follows are resolved relative to the parent. This caused issues when we tried to reuse oldLock but couldn't for some reason (read: mustRefetch is true), in that case the follows were resolved incorrectly. Fix this by passing the correct parent, and adding some tests to prevent this particular regression from happening again. Closes https://github.com/NixOS/nix/issues/5697 --- src/libexpr/flake/flake.cc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src/libexpr') diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc index 06136579e..33d253eee 100644 --- a/src/libexpr/flake/flake.cc +++ b/src/libexpr/flake/flake.cc @@ -480,11 +480,16 @@ LockedFlake lockFlake( } } + LockParent newParent { + .path = inputPath, + .absolute = false + }; + computeLocks( mustRefetch ? getFlake(state, oldLock->lockedRef, false, flakeCache).inputs : fakeInputs, - childNode, inputPath, oldLock, parent, parentPath); + childNode, inputPath, oldLock, newParent, parentPath); } else { /* We need to create a new lock file entry. So fetch -- cgit v1.2.3 From 90700736c7744b97ab69a4fadcd56fa242ec617f Mon Sep 17 00:00:00 2001 From: Silvan Mosberger Date: Thu, 2 Dec 2021 17:46:44 +0100 Subject: Introduce builtins.groupBy primop This function is very useful in nixpkgs, but its implementation in Nix itself is rather slow due to it requiring a lot of attribute set and list appends. --- src/libexpr/primops.cc | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/libexpr/value.hh | 2 ++ 2 files changed, 52 insertions(+) (limited to 'src/libexpr') diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index d1f4d9009..66af373d7 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -2928,6 +2928,56 @@ static RegisterPrimOp primop_partition({ .fun = prim_partition, }); +static void prim_groupBy(EvalState & state, const Pos & pos, Value * * args, Value & v) +{ + state.forceFunction(*args[0], pos); + state.forceList(*args[1], pos); + + ValueVectorMap attrs; + + for (auto vElem : args[1]->listItems()) { + Value res; + state.callFunction(*args[0], *vElem, res, pos); + string name = state.forceStringNoCtx(res, pos); + Symbol sym = state.symbols.create(name); + auto vector = attrs.try_emplace(sym, ValueVector()).first; + vector->second.push_back(vElem); + } + + state.mkAttrs(v, attrs.size()); + + for (auto & i : attrs) { + Value * list = state.allocAttr(v, i.first); + auto size = i.second.size(); + state.mkList(*list, size); + memcpy(list->listElems(), i.second.data(), sizeof(Value *) * size); + } +} + +static RegisterPrimOp primop_groupBy({ + .name = "__groupBy", + .args = {"f", "list"}, + .doc = R"( + Groups elements of *list* together by the string returned from the + function *f* called on each element. It returns an attribute set + where each attribute value contains the elements of *list* that are + mapped to the same corresponding attribute name returned by *f*. + + For example, + + ```nix + builtins.groupBy (builtins.substring 0 1) ["foo" "bar" "baz"] + ``` + + evaluates to + + ```nix + { b = [ "bar" "baz" ]; f = [ "foo" ]; } + ``` + )", + .fun = prim_groupBy, +}); + static void prim_concatMap(EvalState & state, const Pos & pos, Value * * args, Value & v) { state.forceFunction(*args[0], pos); diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh index 4b43e47ae..6b4f3c0ae 100644 --- a/src/libexpr/value.hh +++ b/src/libexpr/value.hh @@ -425,9 +425,11 @@ void mkPath(Value & v, const char * s); #if HAVE_BOEHMGC typedef std::vector > ValueVector; typedef std::map, traceable_allocator > > ValueMap; +typedef std::map, traceable_allocator > > ValueVectorMap; #else typedef std::vector ValueVector; typedef std::map ValueMap; +typedef std::map ValueVectorMap; #endif -- cgit v1.2.3 From b6cc0a704d8c1432e230ff65d4b74ea7114a730b Mon Sep 17 00:00:00 2001 From: Tom Bereknyei Date: Fri, 3 Dec 2021 10:53:41 -0500 Subject: flakes: search up to git or filesystem boundary While parsing a flakeref, upon not finding a flake.nix, search upwards until git or filesystem boundary. --- src/libexpr/flake/flakeref.cc | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/flake/flakeref.cc b/src/libexpr/flake/flakeref.cc index 29128d789..074727f06 100644 --- a/src/libexpr/flake/flakeref.cc +++ b/src/libexpr/flake/flakeref.cc @@ -117,8 +117,27 @@ std::pair parseFlakeRefWithFragment( if (!S_ISDIR(lstat(path).st_mode)) throw BadURL("path '%s' is not a flake (because it's not a directory)", path); - if (!allowMissing && !pathExists(path + "/flake.nix")) - throw BadURL("path '%s' is not a flake (because it doesn't contain a 'flake.nix' file)", path); + if (!allowMissing && !pathExists(path + "/flake.nix")){ + notice("path '%s' does not contain a 'flake.nix', searching up",path); + + // Save device to detect filesystem boundary + dev_t device = lstat(path).st_dev; + bool found = false; + while (path != "/") { + if (pathExists(path + "/flake.nix")) { + found = true; + break; + } else if (pathExists(path + "/.git")) + throw Error("unable to find a flake before encountering git boundary at '%s'", path); + else { + if (lstat(path).st_dev != device) + throw Error("unable to find a flake before encountering filesystem boundary at '%s'", path); + } + path = dirOf(path); + } + if (!found) + throw BadURL("could not find a flake.nix file"); + } auto flakeRoot = path; std::string subdir; -- cgit v1.2.3 From 32a62b0d25116ffe0d66ecaf6a479fbc915d196e Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 13 Dec 2021 20:54:43 +0100 Subject: Re-allow inputs.x.url = "/path/to/file" --- src/libexpr/flake/flake.cc | 8 ++--- src/libexpr/flake/flakeref.cc | 77 ++++++++++++++++++++++++------------------- src/libexpr/flake/flakeref.hh | 10 ++++-- 3 files changed, 55 insertions(+), 40 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc index 33d253eee..f598400fc 100644 --- a/src/libexpr/flake/flake.cc +++ b/src/libexpr/flake/flake.cc @@ -155,7 +155,7 @@ static FlakeInput parseFlakeInput(EvalState & state, if (!attrs.empty()) throw Error("unexpected flake input attribute '%s', at %s", attrs.begin()->first, pos); if (url) - input.ref = parseFlakeRef(*url, baseDir, true); + input.ref = parseFlakeRef(*url, baseDir, true, input.isFlake); } if (!input.follows && !input.ref) @@ -194,8 +194,8 @@ static Flake getFlake( state, originalRef, allowLookup, flakeCache); // Guard against symlink attacks. - auto flakeDir = canonPath(sourceInfo.actualPath + "/" + lockedRef.subdir); - auto flakeFile = canonPath(flakeDir + "/flake.nix"); + auto flakeDir = canonPath(sourceInfo.actualPath + "/" + lockedRef.subdir, true); + auto flakeFile = canonPath(flakeDir + "/flake.nix", true); if (!isInDir(flakeFile, sourceInfo.actualPath)) throw Error("'flake.nix' file of flake '%s' escapes from '%s'", lockedRef, state.store->printStorePath(sourceInfo.storePath)); @@ -570,7 +570,7 @@ LockedFlake lockFlake( }; // Bring in the current ref for relative path resolution if we have it - auto parentPath = canonPath(flake.sourceInfo->actualPath + "/" + flake.lockedRef.subdir); + auto parentPath = canonPath(flake.sourceInfo->actualPath + "/" + flake.lockedRef.subdir, true); computeLocks( flake.inputs, newLockFile.root, {}, diff --git a/src/libexpr/flake/flakeref.cc b/src/libexpr/flake/flakeref.cc index 29128d789..c3b74e0fe 100644 --- a/src/libexpr/flake/flakeref.cc +++ b/src/libexpr/flake/flakeref.cc @@ -48,9 +48,12 @@ FlakeRef FlakeRef::resolve(ref store) const } FlakeRef parseFlakeRef( - const std::string & url, const std::optional & baseDir, bool allowMissing) + const std::string & url, + const std::optional & baseDir, + bool allowMissing, + bool isFlake) { - auto [flakeRef, fragment] = parseFlakeRefWithFragment(url, baseDir, allowMissing); + auto [flakeRef, fragment] = parseFlakeRefWithFragment(url, baseDir, allowMissing, isFlake); if (fragment != "") throw Error("unexpected fragment '%s' in flake reference '%s'", fragment, url); return flakeRef; @@ -67,7 +70,10 @@ std::optional maybeParseFlakeRef( } std::pair parseFlakeRefWithFragment( - const std::string & url, const std::optional & baseDir, bool allowMissing) + const std::string & url, + const std::optional & baseDir, + bool allowMissing, + bool isFlake) { using namespace fetchers; @@ -112,46 +118,49 @@ std::pair parseFlakeRefWithFragment( to 'baseDir'). If so, search upward to the root of the repo (i.e. the directory containing .git). */ - path = absPath(path, baseDir, true); + path = absPath(path, baseDir); - if (!S_ISDIR(lstat(path).st_mode)) - throw BadURL("path '%s' is not a flake (because it's not a directory)", path); + if (isFlake) { - if (!allowMissing && !pathExists(path + "/flake.nix")) - throw BadURL("path '%s' is not a flake (because it doesn't contain a 'flake.nix' file)", path); + if (!S_ISDIR(lstat(path).st_mode)) + throw BadURL("path '%s' is not a flake (because it's not a directory)", path); - auto flakeRoot = path; - std::string subdir; + if (!allowMissing && !pathExists(path + "/flake.nix")) + throw BadURL("path '%s' is not a flake (because it doesn't contain a 'flake.nix' file)", path); - while (flakeRoot != "/") { - if (pathExists(flakeRoot + "/.git")) { - auto base = std::string("git+file://") + flakeRoot; + auto flakeRoot = path; + std::string subdir; - auto parsedURL = ParsedURL{ - .url = base, // FIXME - .base = base, - .scheme = "git+file", - .authority = "", - .path = flakeRoot, - .query = decodeQuery(match[2]), - }; + while (flakeRoot != "/") { + if (pathExists(flakeRoot + "/.git")) { + auto base = std::string("git+file://") + flakeRoot; - if (subdir != "") { - if (parsedURL.query.count("dir")) - throw Error("flake URL '%s' has an inconsistent 'dir' parameter", url); - parsedURL.query.insert_or_assign("dir", subdir); - } + auto parsedURL = ParsedURL{ + .url = base, // FIXME + .base = base, + .scheme = "git+file", + .authority = "", + .path = flakeRoot, + .query = decodeQuery(match[2]), + }; - if (pathExists(flakeRoot + "/.git/shallow")) - parsedURL.query.insert_or_assign("shallow", "1"); + if (subdir != "") { + if (parsedURL.query.count("dir")) + throw Error("flake URL '%s' has an inconsistent 'dir' parameter", url); + parsedURL.query.insert_or_assign("dir", subdir); + } - return std::make_pair( - FlakeRef(Input::fromURL(parsedURL), get(parsedURL.query, "dir").value_or("")), - fragment); - } + if (pathExists(flakeRoot + "/.git/shallow")) + parsedURL.query.insert_or_assign("shallow", "1"); - subdir = std::string(baseNameOf(flakeRoot)) + (subdir.empty() ? "" : "/" + subdir); - flakeRoot = dirOf(flakeRoot); + return std::make_pair( + FlakeRef(Input::fromURL(parsedURL), get(parsedURL.query, "dir").value_or("")), + fragment); + } + + subdir = std::string(baseNameOf(flakeRoot)) + (subdir.empty() ? "" : "/" + subdir); + flakeRoot = dirOf(flakeRoot); + } } } else { diff --git a/src/libexpr/flake/flakeref.hh b/src/libexpr/flake/flakeref.hh index 0292eb210..1fddfd9a0 100644 --- a/src/libexpr/flake/flakeref.hh +++ b/src/libexpr/flake/flakeref.hh @@ -62,13 +62,19 @@ struct FlakeRef std::ostream & operator << (std::ostream & str, const FlakeRef & flakeRef); FlakeRef parseFlakeRef( - const std::string & url, const std::optional & baseDir = {}, bool allowMissing = false); + const std::string & url, + const std::optional & baseDir = {}, + bool allowMissing = false, + bool isFlake = true); std::optional maybeParseFlake( const std::string & url, const std::optional & baseDir = {}); std::pair parseFlakeRefWithFragment( - const std::string & url, const std::optional & baseDir = {}, bool allowMissing = false); + const std::string & url, + const std::optional & baseDir = {}, + bool allowMissing = false, + bool isFlake = true); std::optional> maybeParseFlakeRefWithFragment( const std::string & url, const std::optional & baseDir = {}); -- cgit v1.2.3 From ab902521b131c6a642d8cdcfe28d61b5dfe764a2 Mon Sep 17 00:00:00 2001 From: regnat Date: Fri, 12 Nov 2021 16:28:39 +0100 Subject: Allow paths in flake local settings Fix #5505 --- src/libexpr/flake/flake.cc | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/libexpr') diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc index b15878d5c..ed753c7e4 100644 --- a/src/libexpr/flake/flake.cc +++ b/src/libexpr/flake/flake.cc @@ -251,6 +251,10 @@ static Flake getFlake( forceTrivialValue(state, *setting.value, *setting.pos); if (setting.value->type() == nString) flake.config.settings.insert({setting.name, state.forceStringNoCtx(*setting.value, *setting.pos)}); + else if (setting.value->type() == nPath) { + PathSet emptyContext = {}; + flake.config.settings.insert({setting.name, state.coerceToString(*setting.pos, *setting.value, emptyContext, false, true, true)}); + } else if (setting.value->type() == nInt) flake.config.settings.insert({setting.name, state.forceInt(*setting.value, *setting.pos)}); else if (setting.value->type() == nBool) -- cgit v1.2.3 From 10a8b5d3ae65cbf842faa83a91e7805276cb799e Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Fri, 17 Dec 2021 22:03:48 +0100 Subject: Update primops/fromTOML.cc to use toml11 --- src/libexpr/primops/fromTOML.cc | 113 +++++++++++++++++++--------------------- 1 file changed, 53 insertions(+), 60 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/primops/fromTOML.cc b/src/libexpr/primops/fromTOML.cc index 4c6682dfd..8fba0bf04 100644 --- a/src/libexpr/primops/fromTOML.cc +++ b/src/libexpr/primops/fromTOML.cc @@ -1,86 +1,79 @@ #include "primops.hh" #include "eval-inline.hh" -#include "../../cpptoml/cpptoml.h" +#include "../../toml11/toml.hpp" namespace nix { -static void prim_fromTOML(EvalState & state, const Pos & pos, Value * * args, Value & v) +static void prim_fromTOML(EvalState & state, const Pos & pos, Value * * args, Value & val) { - using namespace cpptoml; - auto toml = state.forceStringNoCtx(*args[0], pos); std::istringstream tomlStream(toml); - std::function)> visit; - - visit = [&](Value & v, std::shared_ptr t) { - - if (auto t2 = t->as_table()) { - - size_t size = 0; - for (auto & i : *t2) { (void) i; size++; } - - state.mkAttrs(v, size); - - for (auto & i : *t2) { - auto & v2 = *state.allocAttr(v, state.symbols.create(i.first)); - - if (auto i2 = i.second->as_table_array()) { - size_t size2 = i2->get().size(); - state.mkList(v2, size2); - for (size_t j = 0; j < size2; ++j) - visit(*(v2.listElems()[j] = state.allocValue()), i2->get()[j]); - } - else - visit(v2, i.second); - } + std::function visit; - v.attrs->sort(); - } + visit = [&](Value & v, toml::value t) { - else if (auto t2 = t->as_array()) { - size_t size = t2->get().size(); + switch(t.type()) + { + case toml::value_t::table: + { + auto table = toml::get(t); - state.mkList(v, size); + size_t size = 0; + for (auto & i : table) { (void) i; size++; } - for (size_t i = 0; i < size; ++i) - visit(*(v.listElems()[i] = state.allocValue()), t2->get()[i]); - } + state.mkAttrs(v, size); - // Handle cases like 'a = [[{ a = true }]]', which IMHO should be - // parsed as a array containing an array containing a table, - // but instead are parsed as an array containing a table array - // containing a table. - else if (auto t2 = t->as_table_array()) { - size_t size = t2->get().size(); + for(auto & elem: table) { - state.mkList(v, size); + auto & v2 = *state.allocAttr(v, state.symbols.create(elem.first)); - for (size_t j = 0; j < size; ++j) - visit(*(v.listElems()[j] = state.allocValue()), t2->get()[j]); - } + // TODO: note about creating children here in old code + visit(v2, elem.second); + } + } + break;; + case toml::value_t::array: + { + auto array = toml::get>(t); + + size_t size = array.size(); + state.mkList(v, size); + for (size_t i = 0; i < size; ++i) + visit(*(v.listElems()[i] = state.allocValue()), array[i]); + } + break;; + case toml::value_t::boolean: + mkBool(v, toml::get(t)); + break;; + case toml::value_t::integer: + mkInt(v, toml::get(t)); + break;; + case toml::value_t::floating: + mkFloat(v, toml::get(t)); + break;; + case toml::value_t::string: + mkString(v, toml::get(t)); + break;; + case toml::value_t::local_datetime: + case toml::value_t::offset_datetime: + case toml::value_t::local_date: + case toml::value_t::local_time: + // TODO: convert to string? + throw std::runtime_error("Dates and times are not supported"); + break;; + case toml::value_t::empty: + mkNull(v); + break;; - else if (t->is_value()) { - if (auto val = t->as()) - mkInt(v, val->get()); - else if (auto val = t->as()) - mkFloat(v, val->get()); - else if (auto val = t->as()) - mkBool(v, val->get()); - else if (auto val = t->as()) - mkString(v, val->get()); - else - throw EvalError("unsupported value type in TOML"); } - - else abort(); }; try { - visit(v, parser(tomlStream).parse()); - } catch (std::runtime_error & e) { + visit(val, toml::parse(tomlStream)); // TODO give filename + } catch (std::exception & e) { // TODO: toml::syntax_error throw EvalError({ .msg = hintfmt("while parsing a TOML string: %s", e.what()), .errPos = pos -- cgit v1.2.3 From 5d06836b9f8b675bdbaa1ce2497d704cfe5e2456 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Fri, 17 Dec 2021 23:15:26 +0100 Subject: Use long in fromTOML --- src/libexpr/primops/fromTOML.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/libexpr') diff --git a/src/libexpr/primops/fromTOML.cc b/src/libexpr/primops/fromTOML.cc index 8fba0bf04..4ec64daf2 100644 --- a/src/libexpr/primops/fromTOML.cc +++ b/src/libexpr/primops/fromTOML.cc @@ -49,7 +49,7 @@ static void prim_fromTOML(EvalState & state, const Pos & pos, Value * * args, Va mkBool(v, toml::get(t)); break;; case toml::value_t::integer: - mkInt(v, toml::get(t)); + mkInt(v, toml::get(t)); break;; case toml::value_t::floating: mkFloat(v, toml::get(t)); -- cgit v1.2.3 From 9dea5768ef0e3d11725926a96eeadac81c0e2543 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Mon, 20 Dec 2021 21:39:37 +0100 Subject: Clean up toml parsing code --- src/libexpr/primops/fromTOML.cc | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/primops/fromTOML.cc b/src/libexpr/primops/fromTOML.cc index 4ec64daf2..bfea1ce34 100644 --- a/src/libexpr/primops/fromTOML.cc +++ b/src/libexpr/primops/fromTOML.cc @@ -29,8 +29,6 @@ static void prim_fromTOML(EvalState & state, const Pos & pos, Value * * args, Va for(auto & elem: table) { auto & v2 = *state.allocAttr(v, state.symbols.create(elem.first)); - - // TODO: note about creating children here in old code visit(v2, elem.second); } } @@ -61,7 +59,7 @@ static void prim_fromTOML(EvalState & state, const Pos & pos, Value * * args, Va case toml::value_t::offset_datetime: case toml::value_t::local_date: case toml::value_t::local_time: - // TODO: convert to string? + // We fail since Nix doesn't have date and time types throw std::runtime_error("Dates and times are not supported"); break;; case toml::value_t::empty: @@ -72,7 +70,7 @@ static void prim_fromTOML(EvalState & state, const Pos & pos, Value * * args, Va }; try { - visit(val, toml::parse(tomlStream)); // TODO give filename + visit(val, toml::parse(tomlStream, "fromTOML" /* the "filename" */)); } catch (std::exception & e) { // TODO: toml::syntax_error throw EvalError({ .msg = hintfmt("while parsing a TOML string: %s", e.what()), -- cgit v1.2.3 From 09b245690a9ab40847e3faa96646e4bd6ce033e0 Mon Sep 17 00:00:00 2001 From: pennae Date: Mon, 20 Dec 2021 13:28:54 +0100 Subject: bulk-allocate Value instances in the evaluator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit calling GC_malloc for each value is significantly more expensive than allocating a bunch of values at once with GC_malloc_many. "a bunch" here is a GC block size, ie 16KiB or less. this gives a 1.5% performance boost when evaluating our nixos system. tested with nix eval --raw --impure --expr 'with import {}; system' # on master Time (mean ± σ): 3.335 s ± 0.007 s [User: 2.774 s, System: 0.293 s] Range (min … max): 3.315 s … 3.347 s 50 runs # with this change Time (mean ± σ): 3.288 s ± 0.006 s [User: 2.728 s, System: 0.292 s] Range (min … max): 3.274 s … 3.307 s 50 runs --- src/libexpr/eval.cc | 17 ++++++++++++++++- src/libexpr/eval.hh | 3 +++ 2 files changed, 19 insertions(+), 1 deletion(-) (limited to 'src/libexpr') diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 8cf4d9549..a95726f5f 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -821,8 +821,23 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval) Value * EvalState::allocValue() { + /* We use the boehm batch allocator to speed up allocations of Values (of which there are many). + GC_malloc_many returns a linked list of objects of the given size, where the first word + of each object is also the pointer to the next object in the list. This also means that we + have to explicitly clear the first word of every object we take. */ + if (!valueAllocCache) { + valueAllocCache = GC_malloc_many(sizeof(Value)); + if (!valueAllocCache) throw std::bad_alloc(); + } + + /* GC_NEXT is a convenience macro for accessing the first word of an object. + Take the first list item, advance the list to the next item, and clear the next pointer. */ + void * p = valueAllocCache; + GC_PTR_STORE_AND_DIRTY(&valueAllocCache, GC_NEXT(p)); + GC_NEXT(p) = nullptr; + nrValues++; - auto v = (Value *) allocBytes(sizeof(Value)); + auto v = (Value *) p; return v; } diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 1aab8e166..cc63294c6 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -133,6 +133,9 @@ private: /* Cache used by prim_match(). */ std::shared_ptr regexCache; + /* Allocation cache for GC'd Value objects. */ + void * valueAllocCache = nullptr; + public: EvalState( -- cgit v1.2.3 From cbbd21ec073781f68029daff153dac2516dafc23 Mon Sep 17 00:00:00 2001 From: regnat Date: Tue, 21 Dec 2021 08:42:19 +0100 Subject: Factor out the path realisation bit of IFD --- src/libexpr/primops.cc | 75 +++++++++++++++++++++++++------------------------- 1 file changed, 38 insertions(+), 37 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 66af373d7..5f00fad1c 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -76,6 +76,19 @@ void EvalState::realiseContext(const PathSet & context) } } +static Path realisePath(EvalState & state, const Pos & pos, Value & v, bool requireAbsolutePath = true) +{ + PathSet context; + + Path path = requireAbsolutePath + ? state.coerceToPath(pos, v, context) + : state.coerceToString(pos, v, context, false, false); + + state.realiseContext(context); + + return state.checkSourcePath(state.toRealPath(path, context)); +} + /* Add and attribute to the given attribute map from the output name to the output path, or a placeholder. @@ -109,11 +122,9 @@ static void mkOutputString(EvalState & state, Value & v, argument. */ static void import(EvalState & state, const Pos & pos, Value & vPath, Value * vScope, Value & v) { - PathSet context; - Path path = state.coerceToPath(pos, vPath, context); - + Path path; try { - state.realiseContext(context); + path = realisePath(state, pos, vPath); } catch (InvalidPathError & e) { throw EvalError({ .msg = hintfmt("cannot import '%1%', since path '%2%' is not valid", path, e.path), @@ -124,8 +135,6 @@ static void import(EvalState & state, const Pos & pos, Value & vPath, Value * vS throw; } - Path realPath = state.checkSourcePath(state.toRealPath(path, context)); - // FIXME auto isValidDerivationInStore = [&]() -> std::optional { if (!state.store->isStorePath(path)) @@ -177,7 +186,7 @@ static void import(EvalState & state, const Pos & pos, Value & vPath, Value * vS else { if (!vScope) - state.evalFile(realPath, v); + state.evalFile(path, v); else { state.forceAttrs(*vScope); @@ -195,8 +204,8 @@ static void import(EvalState & state, const Pos & pos, Value & vPath, Value * vS // No need to call staticEnv.sort(), because // args[0]->attrs is already sorted. - printTalkative("evaluating file '%1%'", realPath); - Expr * e = state.parseExprFromFile(resolveExprPath(realPath), staticEnv); + printTalkative("evaluating file '%1%'", path); + Expr * e = state.parseExprFromFile(resolveExprPath(path), staticEnv); e->eval(state, *env, v); } @@ -281,22 +290,19 @@ extern "C" typedef void (*ValueInitializer)(EvalState & state, Value & v); /* Load a ValueInitializer from a DSO and return whatever it initializes */ void prim_importNative(EvalState & state, const Pos & pos, Value * * args, Value & v) { - PathSet context; - Path path = state.coerceToPath(pos, *args[0], context); - + Path path; try { - state.realiseContext(context); + path = realisePath(state, pos, *args[0]); } catch (InvalidPathError & e) { throw EvalError({ - .msg = hintfmt( - "cannot import '%1%', since path '%2%' is not valid", - path, e.path), + .msg = hintfmt("cannot import '%1%', since path '%2%' is not valid", path, e.path), .errPos = pos }); + } catch (Error & e) { + e.addTrace(pos, "while importing '%s'", path); + throw; } - path = state.checkSourcePath(path); - string sym = state.forceStringNoCtx(*args[1], pos); void *handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL); @@ -1349,10 +1355,9 @@ static RegisterPrimOp primop_storePath({ static void prim_pathExists(EvalState & state, const Pos & pos, Value * * args, Value & v) { - PathSet context; - Path path = state.coerceToPath(pos, *args[0], context); + Path path; try { - state.realiseContext(context); + path = realisePath(state, pos, *args[0]); } catch (InvalidPathError & e) { throw EvalError({ .msg = hintfmt( @@ -1363,7 +1368,7 @@ static void prim_pathExists(EvalState & state, const Pos & pos, Value * * args, } try { - mkBool(v, pathExists(state.checkSourcePath(path))); + mkBool(v, pathExists(path)); } catch (SysError & e) { /* Don't give away info from errors while canonicalising ‘path’ in restricted mode. */ @@ -1426,17 +1431,16 @@ static RegisterPrimOp primop_dirOf({ /* Return the contents of a file as a string. */ static void prim_readFile(EvalState & state, const Pos & pos, Value * * args, Value & v) { - PathSet context; - Path path = state.coerceToPath(pos, *args[0], context); + Path path; try { - state.realiseContext(context); + path = realisePath(state, pos, *args[0]); } catch (InvalidPathError & e) { throw EvalError({ .msg = hintfmt("cannot read '%1%', since path '%2%' is not valid", path, e.path), .errPos = pos }); } - string s = readFile(state.checkSourcePath(state.toRealPath(path, context))); + string s = readFile(path); if (s.find((char) 0) != string::npos) throw Error("the contents of the file '%1%' cannot be represented as a Nix string", path); mkString(v, s.c_str()); @@ -1475,11 +1479,10 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va pos ); - PathSet context; - string path = state.coerceToString(pos, *i->value, context, false, false); + Path path; try { - state.realiseContext(context); + path = realisePath(state, pos, *i->value, false); } catch (InvalidPathError & e) { throw EvalError({ .msg = hintfmt("cannot find '%1%', since path '%2%' is not valid", path, e.path), @@ -1512,15 +1515,14 @@ static void prim_hashFile(EvalState & state, const Pos & pos, Value * * args, Va .errPos = pos }); - PathSet context; - Path path = state.coerceToPath(pos, *args[1], context); + Path path; try { - state.realiseContext(context); + path = realisePath(state, pos, *args[1]); } catch (InvalidPathError & e) { throw EvalError("cannot read '%s' since path '%s' is not valid, at %s", path, e.path, pos); } - mkString(v, hashFile(*ht, state.checkSourcePath(state.toRealPath(path, context))).to_string(Base16, false)); + mkString(v, hashFile(*ht, path).to_string(Base16, false)); } static RegisterPrimOp primop_hashFile({ @@ -1537,10 +1539,9 @@ static RegisterPrimOp primop_hashFile({ /* Read a directory (without . or ..) */ static void prim_readDir(EvalState & state, const Pos & pos, Value * * args, Value & v) { - PathSet ctx; - Path path = state.coerceToPath(pos, *args[0], ctx); + Path path; try { - state.realiseContext(ctx); + path = realisePath(state, pos, *args[0]); } catch (InvalidPathError & e) { throw EvalError({ .msg = hintfmt("cannot read '%1%', since path '%2%' is not valid", path, e.path), @@ -1548,7 +1549,7 @@ static void prim_readDir(EvalState & state, const Pos & pos, Value * * args, Val }); } - DirEntries entries = readDirectory(state.checkSourcePath(path)); + DirEntries entries = readDirectory(path); state.mkAttrs(v, entries.size()); for (auto & ent : entries) { -- cgit v1.2.3 From d90f9d4b9994dc1f15b9d664ae313f06261d6058 Mon Sep 17 00:00:00 2001 From: regnat Date: Mon, 20 Dec 2021 19:46:55 +0100 Subject: Fix IFD with CA derivations Rewrite the string taken by the IFD-like primops to contain the actual output paths of the derivations rather than the placeholders Fix #5805 --- src/libexpr/eval.hh | 5 ++++- src/libexpr/primops.cc | 43 +++++++++++++++++++++++++++++-------------- 2 files changed, 33 insertions(+), 15 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 1aab8e166..0ba570434 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -350,7 +350,10 @@ public: /* Print statistics. */ void printStats(); - void realiseContext(const PathSet & context); + /* Realise the given context, and return a mapping from the placeholders + * used to construct the associated value to their final store path + */ + [[nodiscard]] StringMap realiseContext(const PathSet & context); private: diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 5f00fad1c..aff8f951e 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -35,9 +35,10 @@ namespace nix { InvalidPathError::InvalidPathError(const Path & path) : EvalError("path '%s' is not valid", path), path(path) {} -void EvalState::realiseContext(const PathSet & context) +StringMap EvalState::realiseContext(const PathSet & context) { std::vector drvs; + StringMap res; for (auto & i : context) { auto [ctxS, outputName] = decodeContext(i); @@ -46,10 +47,12 @@ void EvalState::realiseContext(const PathSet & context) throw InvalidPathError(store->printStorePath(ctx)); if (!outputName.empty() && ctx.isDerivation()) { drvs.push_back({ctx, {outputName}}); + } else { + res.insert_or_assign(ctxS, ctxS); } } - if (drvs.empty()) return; + if (drvs.empty()) return {}; if (!evalSettings.enableImportFromDerivation) throw Error( @@ -61,19 +64,29 @@ void EvalState::realiseContext(const PathSet & context) for (auto & d : drvs) buildReqs.emplace_back(DerivedPath { d }); store->buildPaths(buildReqs); + /* Get all the output paths corresponding to the placeholders we had */ + for (auto & [drvPath, outputs] : drvs) { + auto outputPaths = store->queryDerivationOutputMap(drvPath); + for (auto & outputName : outputs) { + if (outputPaths.count(outputName) == 0) + throw Error("derivation '%s' does not have an output named '%s'", + store->printStorePath(drvPath), outputName); + res.insert_or_assign( + downstreamPlaceholder(*store, drvPath, outputName), + store->printStorePath(outputPaths.at(outputName)) + ); + } + } + /* Add the output of this derivations to the allowed paths. */ if (allowedPaths) { - for (auto & [drvPath, outputs] : drvs) { - auto outputPaths = store->queryDerivationOutputMap(drvPath); - for (auto & outputName : outputs) { - if (outputPaths.count(outputName) == 0) - throw Error("derivation '%s' does not have an output named '%s'", - store->printStorePath(drvPath), outputName); - allowPath(outputPaths.at(outputName)); - } + for (auto & [_placeholder, outputPath] : res) { + allowPath(outputPath); } } + + return res; } static Path realisePath(EvalState & state, const Pos & pos, Value & v, bool requireAbsolutePath = true) @@ -84,9 +97,10 @@ static Path realisePath(EvalState & state, const Pos & pos, Value & v, bool requ ? state.coerceToPath(pos, v, context) : state.coerceToString(pos, v, context, false, false); - state.realiseContext(context); + StringMap rewrites = state.realiseContext(context); - return state.checkSourcePath(state.toRealPath(path, context)); + return state.checkSourcePath( + state.toRealPath(rewriteStrings(path, rewrites), context)); } /* Add and attribute to the given attribute map from the output name to @@ -344,7 +358,7 @@ void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v) for (unsigned int i = 1; i < args[0]->listSize(); ++i) commandArgs.emplace_back(state.coerceToString(pos, *elems[i], context, false, false)); try { - state.realiseContext(context); + auto _ = state.realiseContext(context); // FIXME: Handle CA derivations } catch (InvalidPathError & e) { throw EvalError({ .msg = hintfmt("cannot execute '%1%', since path '%2%' is not valid", @@ -1876,7 +1890,8 @@ static void addPath( try { // FIXME: handle CA derivation outputs (where path needs to // be rewritten to the actual output). - state.realiseContext(context); + auto rewrites = state.realiseContext(context); + path = state.toRealPath(rewriteStrings(path, rewrites), context); StorePathSet refs; -- cgit v1.2.3 From dc89dfa7b3f8fa7368cd4c5e20891e60e418722b Mon Sep 17 00:00:00 2001 From: regnat Date: Thu, 23 Dec 2021 10:35:09 +0100 Subject: Properly return false on `builtins.pathExists /someNonAllowedPath` Follow-up from https://github.com/NixOS/nix/pull/5807 to fix https://github.com/NixOS/nix/pull/5807#issuecomment-1000135394 --- src/libexpr/primops.cc | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index aff8f951e..62c21c7c5 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -89,18 +89,28 @@ StringMap EvalState::realiseContext(const PathSet & context) return res; } -static Path realisePath(EvalState & state, const Pos & pos, Value & v, bool requireAbsolutePath = true) +struct RealisePathFlags { + // Whether to check whether the path is a valid absolute path + bool requireAbsolutePath = true; + // Whether to check that the path is allowed in pure eval mode + bool checkForPureEval = true; +}; + +static Path realisePath(EvalState & state, const Pos & pos, Value & v, const RealisePathFlags flags = {}) { PathSet context; - Path path = requireAbsolutePath + Path path = flags.requireAbsolutePath ? state.coerceToPath(pos, v, context) : state.coerceToString(pos, v, context, false, false); StringMap rewrites = state.realiseContext(context); - return state.checkSourcePath( - state.toRealPath(rewriteStrings(path, rewrites), context)); + auto realPath = state.toRealPath(rewriteStrings(path, rewrites), context); + + return flags.checkForPureEval + ? state.checkSourcePath(realPath) + : realPath; } /* Add and attribute to the given attribute map from the output name to @@ -1371,7 +1381,12 @@ static void prim_pathExists(EvalState & state, const Pos & pos, Value * * args, { Path path; try { - path = realisePath(state, pos, *args[0]); + // We don’t check the path right now, because we don’t want to throw if + // the path isn’t allowed, but just return false + // (and we can’t just catch the exception here because we still want to + // throw if something in the evaluation of `*args[0]` tries to access an + // unauthorized path) + path = realisePath(state, pos, *args[0], { .checkForPureEval = false }); } catch (InvalidPathError & e) { throw EvalError({ .msg = hintfmt( @@ -1382,7 +1397,7 @@ static void prim_pathExists(EvalState & state, const Pos & pos, Value * * args, } try { - mkBool(v, pathExists(path)); + mkBool(v, pathExists(state.checkSourcePath(path))); } catch (SysError & e) { /* Don't give away info from errors while canonicalising ‘path’ in restricted mode. */ @@ -1496,7 +1511,7 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va Path path; try { - path = realisePath(state, pos, *i->value, false); + path = realisePath(state, pos, *i->value, { .requireAbsolutePath = false }); } catch (InvalidPathError & e) { throw EvalError({ .msg = hintfmt("cannot find '%1%', since path '%2%' is not valid", path, e.path), -- cgit v1.2.3 From b16643b6fc9abb5c243a1d83cfe0da93c441bb2c Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Thu, 23 Dec 2021 14:12:48 +0100 Subject: Use int64_t and NixFloat in fromTOML types This makes sure that values parsed from TOML have a proper size. Using e.g. `double` caused issues on i686 where the size of `double` (32bit) was too small to accommodate some values. --- src/libexpr/primops/fromTOML.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/primops/fromTOML.cc b/src/libexpr/primops/fromTOML.cc index bfea1ce34..221c40db9 100644 --- a/src/libexpr/primops/fromTOML.cc +++ b/src/libexpr/primops/fromTOML.cc @@ -47,10 +47,10 @@ static void prim_fromTOML(EvalState & state, const Pos & pos, Value * * args, Va mkBool(v, toml::get(t)); break;; case toml::value_t::integer: - mkInt(v, toml::get(t)); + mkInt(v, toml::get(t)); break;; case toml::value_t::floating: - mkFloat(v, toml::get(t)); + mkFloat(v, toml::get(t)); break;; case toml::value_t::string: mkString(v, toml::get(t)); -- cgit v1.2.3 From a4ab0a74d97c7c31df69f04870fa56cde89701a3 Mon Sep 17 00:00:00 2001 From: Yorick van Pelt Date: Mon, 27 Dec 2021 13:18:55 +0100 Subject: Fix accidental O(n^2 * log n) performance in NixRepl::addAttrsToScope Only sort once, after adding all of the attrs first. This reduces my `nix repl ''` loading time from 1.07s to 103ms. Fixes #5823 --- src/libexpr/nixexpr.hh | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src/libexpr') diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index c013f5deb..b328b3941 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -368,6 +368,13 @@ struct StaticEnv [](const Vars::value_type & a, const Vars::value_type & b) { return a.first < b.first; }); } + void deduplicate() + { + const auto last = std::unique(vars.begin(), vars.end(), + [] (const Vars::value_type & a, const Vars::value_type & b) { return a.first == b.first; }); + vars.erase(last, vars.end()); + } + Vars::const_iterator find(const Symbol & name) const { Vars::value_type key(name, 0); -- cgit v1.2.3 From 2664a216e57169ec57d7f51be1b8383c1be83fd5 Mon Sep 17 00:00:00 2001 From: Alexander Bantyev Date: Tue, 28 Dec 2021 15:30:06 +0300 Subject: flake.cc: computeLocks: Only verify overrides when they could change When we check for disappeared overrides, we can get "false positives" for follows and overrides which are defined in the dependencies of the flake we are locking, since they are not parsed by parseFlakeInputs. However, at that point we already know that the overrides couldn't have possible been changed if the input itself hasn't changed (since we check that oldLock->originalRef == *input.ref for the input's parent). So, to prevent this, only perform this check when it was possible that the flake changed (e.g. the flake we're locking, or a new input, or the input has changed and mustRefetch == true). --- src/libexpr/flake/flake.cc | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc index b15878d5c..c549c5971 100644 --- a/src/libexpr/flake/flake.cc +++ b/src/libexpr/flake/flake.cc @@ -344,7 +344,8 @@ LockedFlake lockFlake( const InputPath & inputPathPrefix, std::shared_ptr oldNode, const LockParent & parent, - const Path & parentPath)> + const Path & parentPath, + bool trustLock)> computeLocks; computeLocks = [&]( @@ -353,7 +354,8 @@ LockedFlake lockFlake( const InputPath & inputPathPrefix, std::shared_ptr oldNode, const LockParent & parent, - const Path & parentPath) + const Path & parentPath, + bool trustLock) { debug("computing lock file node '%s'", printInputPath(inputPathPrefix)); @@ -464,14 +466,20 @@ LockedFlake lockFlake( .isFlake = (*lockedNode)->isFlake, }); } else if (auto follows = std::get_if<1>(&i.second)) { - auto o = input.overrides.find(i.first); - // If the override disappeared, we have to refetch the flake, - // since some of the inputs may not be present in the lockfile. - if (o == input.overrides.end()) { - mustRefetch = true; - // There's no point populating the rest of the fake inputs, - // since we'll refetch the flake anyways. - break; + if (! trustLock) { + // It is possible that the flake has changed, + // so we must confirm all the follows that are in the lockfile are also in the flake. + auto overridePath(inputPath); + overridePath.push_back(i.first); + auto o = overrides.find(overridePath); + // If the override disappeared, we have to refetch the flake, + // since some of the inputs may not be present in the lockfile. + if (o == overrides.end()) { + mustRefetch = true; + // There's no point populating the rest of the fake inputs, + // since we'll refetch the flake anyways. + break; + } } fakeInputs.emplace(i.first, FlakeInput { .follows = *follows, @@ -482,14 +490,14 @@ LockedFlake lockFlake( LockParent newParent { .path = inputPath, - .absolute = false + .absolute = true }; computeLocks( mustRefetch ? getFlake(state, oldLock->lockedRef, false, flakeCache).inputs : fakeInputs, - childNode, inputPath, oldLock, newParent, parentPath); + childNode, inputPath, oldLock, newParent, parentPath, !mustRefetch); } else { /* We need to create a new lock file entry. So fetch @@ -546,7 +554,7 @@ LockedFlake lockFlake( ? std::dynamic_pointer_cast(oldLock) : LockFile::read( inputFlake.sourceInfo->actualPath + "/" + inputFlake.lockedRef.subdir + "/flake.lock").root, - newParent, localPath); + newParent, localPath, false); } else { @@ -574,7 +582,7 @@ LockedFlake lockFlake( computeLocks( flake.inputs, newLockFile.root, {}, - lockFlags.recreateLockFile ? nullptr : oldLockFile.root, parent, parentPath); + lockFlags.recreateLockFile ? nullptr : oldLockFile.root, parent, parentPath, false); for (auto & i : lockFlags.inputOverrides) if (!overridesUsed.count(i.first)) -- cgit v1.2.3 From 92c7d33ee32b9597fedee5f7371de54c0a10a5a5 Mon Sep 17 00:00:00 2001 From: Lily Foster Date: Tue, 28 Dec 2021 10:10:58 -0500 Subject: Sort attrs from tables in fromTOML This was dropped in 10a8b5d for the migration from cpptoml to toml11 but seems to be necessary for the attrsets to work correctly. Fixes #5833 --- src/libexpr/primops/fromTOML.cc | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/libexpr') diff --git a/src/libexpr/primops/fromTOML.cc b/src/libexpr/primops/fromTOML.cc index 221c40db9..30466fc5b 100644 --- a/src/libexpr/primops/fromTOML.cc +++ b/src/libexpr/primops/fromTOML.cc @@ -31,6 +31,8 @@ static void prim_fromTOML(EvalState & state, const Pos & pos, Value * * args, Va auto & v2 = *state.allocAttr(v, state.symbols.create(elem.first)); visit(v2, elem.second); } + + v.attrs->sort(); } break;; case toml::value_t::array: -- cgit v1.2.3 From d0c8e9254e8a8271a3f044ee69774096e57a74bb Mon Sep 17 00:00:00 2001 From: Sebastian Ullrich Date: Wed, 29 Dec 2021 18:42:02 +0100 Subject: Fix IFD with chroot store --- src/libexpr/primops.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/libexpr') diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 62c21c7c5..7c964bd0d 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -82,7 +82,7 @@ StringMap EvalState::realiseContext(const PathSet & context) paths. */ if (allowedPaths) { for (auto & [_placeholder, outputPath] : res) { - allowPath(outputPath); + allowPath(store->toRealPath(outputPath)); } } -- cgit v1.2.3 From 00c993f48b1c752656d8cdd2c25e38f9ba572128 Mon Sep 17 00:00:00 2001 From: pennae Date: Sat, 25 Dec 2021 15:29:49 +0100 Subject: add zipAttrsWith primop nixpkgs can save a good bit of eval memory with this primop. zipAttrsWith is used quite a bit around nixpkgs (eg in the form of recursiveUpdate), but the most costly application for this primop is in the module system. it improves the implementation of zipAttrsWith from nixpkgs by not checking an attribute multiple times if it occurs more than once in the input list, allocates less values and set elements, and just avoids many a temporary object in general. nixpkgs has a more generic version of this operation, zipAttrsWithNames, but this version is only used once so isn't suitable for being the base of a new primop. if it were to be used more we should add a second primop instead. --- src/libexpr/primops.cc | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) (limited to 'src/libexpr') diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 7c964bd0d..ab99962f7 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -2503,6 +2503,90 @@ static RegisterPrimOp primop_mapAttrs({ .fun = prim_mapAttrs, }); +static void prim_zipAttrsWith(EvalState & state, const Pos & pos, Value * * args, Value & v) +{ + // we will first count how many values are present for each given key. + // we then allocate a single attrset and pre-populate it with lists of + // appropriate sizes, stash the pointers to the list elements of each, + // and populate the lists. after that we replace the list in the every + // attribute with the merge function application. this way we need not + // use (slightly slower) temporary storage the GC does not know about. + + std::map> attrsSeen; + + state.forceFunction(*args[0], pos); + state.forceList(*args[1], pos); + const auto listSize = args[1]->listSize(); + const auto listElems = args[1]->listElems(); + + for (unsigned int n = 0; n < listSize; ++n) { + Value * vElem = listElems[n]; + try { + state.forceAttrs(*vElem); + for (auto & attr : *vElem->attrs) + attrsSeen[attr.name].first++; + } catch (TypeError & e) { + e.addTrace(pos, hintfmt("while invoking '%s'", "zipAttrsWith")); + throw; + } + } + + state.mkAttrs(v, attrsSeen.size()); + for (auto & [sym, elem] : attrsSeen) { + Value * list = state.allocAttr(v, sym); + state.mkList(*list, elem.first); + elem.second = list->listElems(); + } + + for (unsigned int n = 0; n < listSize; ++n) { + Value * vElem = listElems[n]; + for (auto & attr : *vElem->attrs) + *attrsSeen[attr.name].second++ = attr.value; + } + + for (auto & attr : *v.attrs) { + Value * name = state.allocValue(); + mkString(*name, attr.name); + Value * call1 = state.allocValue(); + mkApp(*call1, *args[0], *name); + Value * call2 = state.allocValue(); + mkApp(*call2, *call1, *attr.value); + attr.value = call2; + } +} + +static RegisterPrimOp primop_zipAttrsWith({ + .name = "__zipAttrsWith", + .args = {"f", "list"}, + .doc = R"( + Transpose a list of attribute sets into an attribute set of lists, + then apply `mapAttrs`. + + `f` receives two arguments: the attribute name and a non-empty + list of all values encountered for that attribute name. + + The result is an attribute set where the attribute names are the + union of the attribute names in each element of `list`. The attribute + values are the return values of `f`. + + ```nix + builtins.zipAttrsWith + (name: values: { inherit name values; }) + [ { a = "x"; } { a = "y"; b = "z"; } ] + ``` + + evaluates to + + ``` + { + a = { name = "a"; values = [ "x" "y" ]; }; + b = { name = "b"; values = [ "z" ]; }; + } + ``` + )", + .fun = prim_zipAttrsWith, +}); + /************************************************************* * Lists -- cgit v1.2.3 From 6d9a6d2cc3a20c9047436d174c9f91a2223da220 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 4 Jan 2022 17:39:16 +0100 Subject: Ensure that attrsets are sorted Previously you had to remember to call value->attrs->sort() after populating value->attrs. Now there is a BindingsBuilder helper that wraps Bindings and ensures that sort() is called before you can use it. --- src/libexpr/attr-set.cc | 25 ++++++- src/libexpr/attr-set.hh | 34 ++++++++++ src/libexpr/common-eval-args.cc | 9 ++- src/libexpr/eval.cc | 50 ++++++++------ src/libexpr/eval.hh | 7 +- src/libexpr/get-drvs.cc | 13 ++-- src/libexpr/primops.cc | 122 ++++++++++++++++------------------ src/libexpr/primops/context.cc | 21 +++--- src/libexpr/primops/fetchMercurial.cc | 14 ++-- src/libexpr/primops/fetchTree.cc | 27 ++++---- src/libexpr/primops/fromTOML.cc | 11 ++- src/libexpr/value.hh | 8 +++ 12 files changed, 203 insertions(+), 138 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/attr-set.cc b/src/libexpr/attr-set.cc index b6091c955..d74243f45 100644 --- a/src/libexpr/attr-set.cc +++ b/src/libexpr/attr-set.cc @@ -41,16 +41,39 @@ Value * EvalState::allocAttr(Value & vAttrs, const Symbol & name) } -Value * EvalState::allocAttr(Value & vAttrs, const std::string & name) +Value * EvalState::allocAttr(Value & vAttrs, std::string_view name) { return allocAttr(vAttrs, symbols.create(name)); } +Value & BindingsBuilder::alloc(const Symbol & name, ptr pos) +{ + auto value = state.allocValue(); + bindings->push_back(Attr(name, value, pos)); + return *value; +} + + +Value & BindingsBuilder::alloc(std::string_view name, ptr pos) +{ + return alloc(state.symbols.create(name), pos); +} + + void Bindings::sort() { std::sort(begin(), end()); } +Value & Value::mkAttrs(BindingsBuilder & bindings) +{ + clearValue(); + internalType = tAttrs; + attrs = bindings.finish(); + return *this; +} + + } diff --git a/src/libexpr/attr-set.hh b/src/libexpr/attr-set.hh index 7d6ffc9f3..903289b69 100644 --- a/src/libexpr/attr-set.hh +++ b/src/libexpr/attr-set.hh @@ -113,5 +113,39 @@ public: friend class EvalState; }; +/* A wrapper around Bindings that ensures that its always in sorted + order at the end. The only way to consume a BindingsBuilder is to + call finish(), which sorts the bindings. */ +class BindingsBuilder +{ + EvalState & state; + Bindings * bindings; + +public: + + BindingsBuilder(EvalState & state, Bindings * bindings) + : state(state), bindings(bindings) + { } + + void insert(Symbol name, Value * value, ptr pos = ptr(&noPos)) + { + insert(Attr(name, value, pos)); + } + + void insert(const Attr & attr) + { + bindings->push_back(attr); + } + + Value & alloc(const Symbol & name, ptr pos = ptr(&noPos)); + + Value & alloc(std::string_view name, ptr pos = ptr(&noPos)); + + Bindings * finish() + { + bindings->sort(); + return bindings; + } +}; } diff --git a/src/libexpr/common-eval-args.cc b/src/libexpr/common-eval-args.cc index fb0932c00..ecae2ca1f 100644 --- a/src/libexpr/common-eval-args.cc +++ b/src/libexpr/common-eval-args.cc @@ -73,17 +73,16 @@ MixEvalArgs::MixEvalArgs() Bindings * MixEvalArgs::getAutoArgs(EvalState & state) { - Bindings * res = state.allocBindings(autoArgs.size()); + auto res = state.buildBindings(autoArgs.size()); for (auto & i : autoArgs) { - Value * v = state.allocValue(); + auto v = state.allocValue(); if (i.second[0] == 'E') state.mkThunk_(*v, state.parseExprFromString(string(i.second, 1), absPath("."))); else mkString(*v, string(i.second, 1)); - res->push_back(Attr(state.symbols.create(i.first), v)); + res.insert(state.symbols.create(i.first), v); } - res->sort(); - return res; + return res.finish(); } Path lookupFileArg(EvalState & state, string s) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index a95726f5f..0710ce270 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -772,18 +772,30 @@ void mkString(Value & v, const char * s) } +void Value::mkString(std::string_view s) +{ + mkString(dupStringWithLen(s.data(), s.size())); +} + + Value & mkString(Value & v, std::string_view s, const PathSet & context) { - v.mkString(dupStringWithLen(s.data(), s.size())); + v.mkString(s, context); + return v; +} + + +void Value::mkString(std::string_view s, const PathSet & context) +{ + mkString(s); if (!context.empty()) { size_t n = 0; - v.string.context = (const char * *) + string.context = (const char * *) allocBytes((context.size() + 1) * sizeof(char *)); for (auto & i : context) - v.string.context[n++] = dupString(i.c_str()); - v.string.context[n] = 0; + string.context[n++] = dupString(i.c_str()); + string.context[n] = 0; } - return v; } @@ -882,11 +894,11 @@ void EvalState::mkThunk_(Value & v, Expr * expr) void EvalState::mkPos(Value & v, ptr pos) { if (pos->file.set()) { - mkAttrs(v, 3); - mkString(*allocAttr(v, sFile), pos->file); - mkInt(*allocAttr(v, sLine), pos->line); - mkInt(*allocAttr(v, sColumn), pos->column); - v.attrs->sort(); + auto attrs = buildBindings(3); + attrs.alloc(sFile).mkString(pos->file); + attrs.alloc(sLine).mkInt(pos->line); + attrs.alloc(sColumn).mkInt(pos->column); + v.mkAttrs(attrs); } else mkNull(v); } @@ -1341,7 +1353,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & /* Nope, so show the first unexpected argument to the user. */ for (auto & i : *args[0]->attrs) - if (lambda.formals->argNames.find(i.name) == lambda.formals->argNames.end()) + if (!lambda.formals->argNames.count(i.name)) throwTypeError(pos, "%1% called with unexpected argument '%2%'", lambda, i.name); abort(); // can't happen } @@ -1484,22 +1496,20 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res) return; } - Value * actualArgs = allocValue(); - mkAttrs(*actualArgs, std::max(static_cast(fun.lambda.fun->formals->formals.size()), args.size())); + auto attrs = buildBindings(std::max(static_cast(fun.lambda.fun->formals->formals.size()), args.size())); if (fun.lambda.fun->formals->ellipsis) { // If the formals have an ellipsis (eg the function accepts extra args) pass // all available automatic arguments (which includes arguments specified on // the command line via --arg/--argstr) - for (auto& v : args) { - actualArgs->attrs->push_back(v); - } + for (auto & v : args) + attrs.insert(v); } else { // Otherwise, only pass the arguments that the function accepts for (auto & i : fun.lambda.fun->formals->formals) { Bindings::iterator j = args.find(i.name); if (j != args.end()) { - actualArgs->attrs->push_back(*j); + attrs.insert(*j); } else if (!i.def) { throwMissingArgumentError(i.pos, R"(cannot evaluate a function that has an argument without a value ('%1%') @@ -1512,9 +1522,7 @@ https://nixos.org/manual/nix/stable/#ss-functions.)", i.name); } } - actualArgs->attrs->sort(); - - callFunction(fun, *actualArgs, res, noPos); + callFunction(fun, allocValue()->mkAttrs(attrs), res, noPos); } @@ -1719,7 +1727,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v) auto path = canonPath(s.str()); mkPath(v, path.c_str()); } else - mkString(v, s.str(), context); + v.mkString(s.str(), context); } diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index d7ef7b88a..44ccc6593 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -339,10 +339,15 @@ public: Env & allocEnv(size_t size); Value * allocAttr(Value & vAttrs, const Symbol & name); - Value * allocAttr(Value & vAttrs, const std::string & name); + Value * allocAttr(Value & vAttrs, std::string_view name); Bindings * allocBindings(size_t capacity); + BindingsBuilder buildBindings(size_t capacity) + { + return BindingsBuilder(*this, allocBindings(capacity)); + } + void mkList(Value & v, size_t length); void mkAttrs(Value & v, size_t capacity); void mkThunk_(Value & v, Expr * expr); diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc index ed4c47fbb..25fd9b949 100644 --- a/src/libexpr/get-drvs.cc +++ b/src/libexpr/get-drvs.cc @@ -254,15 +254,14 @@ bool DrvInfo::queryMetaBool(const string & name, bool def) void DrvInfo::setMeta(const string & name, Value * v) { getMeta(); - Bindings * old = meta; - meta = state->allocBindings(1 + (old ? old->size() : 0)); + auto attrs = state->buildBindings(1 + (meta ? meta->size() : 0)); Symbol sym = state->symbols.create(name); - if (old) - for (auto i : *old) + if (meta) + for (auto i : *meta) if (i.name != sym) - meta->push_back(i); - if (v) meta->push_back(Attr(sym, v)); - meta->sort(); + attrs.insert(i); + if (v) attrs.insert(sym, v); + meta = attrs.finish(); } diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index ab99962f7..daf5fc927 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -125,13 +125,15 @@ static Path realisePath(EvalState & state, const Pos & pos, Value & v, const Rea the actual path. The 'drv' and 'drvPath' outputs must correspond. */ -static void mkOutputString(EvalState & state, Value & v, - const StorePath & drvPath, const BasicDerivation & drv, - std::pair o) +static void mkOutputString( + EvalState & state, + BindingsBuilder & attrs, + const StorePath & drvPath, + const BasicDerivation & drv, + const std::pair & o) { auto optOutputPath = o.second.path(*state.store, drv.name, o.first); - mkString( - *state.allocAttr(v, state.symbols.create(o.first)), + attrs.alloc(o.first).mkString( optOutputPath ? state.store->printStorePath(*optOutputPath) /* Downstream we would substitute this for an actual path once @@ -172,23 +174,19 @@ static void import(EvalState & state, const Pos & pos, Value & vPath, Value * vS if (auto optStorePath = isValidDerivationInStore()) { auto storePath = *optStorePath; Derivation drv = state.store->readDerivation(storePath); - Value & w = *state.allocValue(); - state.mkAttrs(w, 3 + drv.outputs.size()); - Value * v2 = state.allocAttr(w, state.sDrvPath); - mkString(*v2, path, {"=" + path}); - v2 = state.allocAttr(w, state.sName); - mkString(*v2, drv.env["name"]); - Value * outputsVal = - state.allocAttr(w, state.symbols.create("outputs")); - state.mkList(*outputsVal, drv.outputs.size()); - unsigned int outputs_index = 0; - - for (const auto & o : drv.outputs) { - mkOutputString(state, w, storePath, drv, o); - outputsVal->listElems()[outputs_index] = state.allocValue(); - mkString(*(outputsVal->listElems()[outputs_index++]), o.first); + auto attrs = state.buildBindings(3 + drv.outputs.size()); + attrs.alloc(state.sDrvPath).mkString(path, {"=" + path}); + attrs.alloc(state.sName).mkString(drv.env["name"]); + auto & outputsVal = attrs.alloc(state.sOutputs); + state.mkList(outputsVal, drv.outputs.size()); + + for (const auto & [i, o] : enumerate(drv.outputs)) { + mkOutputString(state, attrs, storePath, drv, o); + (outputsVal.listElems()[i] = state.allocValue())->mkString(o.first); } - w.attrs->sort(); + + auto w = state.allocValue(); + w->mkAttrs(attrs); if (!state.vImportedDrvToDerivation) { state.vImportedDrvToDerivation = allocRootValue(state.allocValue()); @@ -198,7 +196,7 @@ static void import(EvalState & state, const Pos & pos, Value & vPath, Value * vS } state.forceFunction(**state.vImportedDrvToDerivation, pos); - mkApp(v, **state.vImportedDrvToDerivation, w); + mkApp(v, **state.vImportedDrvToDerivation, *w); state.forceAttrs(v, pos); } @@ -802,16 +800,16 @@ static RegisterPrimOp primop_floor({ * else => {success=false; value=false;} */ static void prim_tryEval(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.mkAttrs(v, 2); + auto attrs = state.buildBindings(2); try { state.forceValue(*args[0], pos); - v.attrs->push_back(Attr(state.sValue, args[0])); - mkBool(*state.allocAttr(v, state.symbols.create("success")), true); + attrs.insert(state.sValue, args[0]); + mkBool(attrs.alloc("success"), true); } catch (AssertionError & e) { - mkBool(*state.allocAttr(v, state.sValue), false); - mkBool(*state.allocAttr(v, state.symbols.create("success")), false); + mkBool(attrs.alloc(state.sValue), false); + mkBool(attrs.alloc("success"), false); } - v.attrs->sort(); + v.mkAttrs(attrs); } static RegisterPrimOp primop_tryEval({ @@ -839,7 +837,7 @@ static RegisterPrimOp primop_tryEval({ static void prim_getEnv(EvalState & state, const Pos & pos, Value * * args, Value & v) { string name = state.forceStringNoCtx(*args[0], pos); - mkString(v, evalSettings.restrictEval || evalSettings.pureEval ? "" : getEnv(name).value_or("")); + v.mkString(evalSettings.restrictEval || evalSettings.pureEval ? "" : getEnv(name).value_or("")); } static RegisterPrimOp primop_getEnv({ @@ -1265,11 +1263,11 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * drvHashes.lock()->insert_or_assign(drvPath, h); } - state.mkAttrs(v, 1 + drv.outputs.size()); - mkString(*state.allocAttr(v, state.sDrvPath), drvPathS, {"=" + drvPathS}); + auto attrs = state.buildBindings(1 + drv.outputs.size()); + attrs.alloc(state.sDrvPath).mkString(drvPathS, {"=" + drvPathS}); for (auto & i : drv.outputs) - mkOutputString(state, v, drvPath, drv, i); - v.attrs->sort(); + mkOutputString(state, attrs, drvPath, drv, i); + v.mkAttrs(attrs); } static RegisterPrimOp primop_derivationStrict(RegisterPrimOp::Info { @@ -1579,20 +1577,20 @@ static void prim_readDir(EvalState & state, const Pos & pos, Value * * args, Val } DirEntries entries = readDirectory(path); - state.mkAttrs(v, entries.size()); + + auto attrs = state.buildBindings(entries.size()); for (auto & ent : entries) { - Value * ent_val = state.allocAttr(v, state.symbols.create(ent.name)); if (ent.type == DT_UNKNOWN) ent.type = getFileType(path + "/" + ent.name); - ent_val->mkString( + attrs.alloc(ent.name).mkString( ent.type == DT_REG ? "regular" : ent.type == DT_DIR ? "directory" : ent.type == DT_LNK ? "symlink" : "unknown"); } - v.attrs->sort(); + v.mkAttrs(attrs); } static RegisterPrimOp primop_readDir({ @@ -2308,7 +2306,7 @@ static void prim_listToAttrs(EvalState & state, const Pos & pos, Value * * args, { state.forceList(*args[0], pos); - state.mkAttrs(v, args[0]->listSize()); + auto attrs = state.buildBindings(args[0]->listSize()); std::set seen; @@ -2334,11 +2332,11 @@ static void prim_listToAttrs(EvalState & state, const Pos & pos, Value * * args, v2->attrs, pos ); - v.attrs->push_back(Attr(sym, j2->value, j2->pos)); + attrs.insert(sym, j2->value, j2->pos); } } - v.attrs->sort(); + v.mkAttrs(attrs); } static RegisterPrimOp primop_listToAttrs({ @@ -2445,14 +2443,11 @@ static void prim_functionArgs(EvalState & state, const Pos & pos, Value * * args return; } - state.mkAttrs(v, args[0]->lambda.fun->formals->formals.size()); - for (auto & i : args[0]->lambda.fun->formals->formals) { + auto attrs = state.buildBindings(args[0]->lambda.fun->formals->formals.size()); + for (auto & i : args[0]->lambda.fun->formals->formals) // !!! should optimise booleans (allocate only once) - Value * value = state.allocValue(); - v.attrs->push_back(Attr(i.name, value, ptr(&i.pos))); - mkBool(*value, i.def); - } - v.attrs->sort(); + mkBool(attrs.alloc(i.name, ptr(&i.pos)), i.def); + v.mkAttrs(attrs); } static RegisterPrimOp primop_functionArgs({ @@ -3003,21 +2998,21 @@ static void prim_partition(EvalState & state, const Pos & pos, Value * * args, V wrong.push_back(vElem); } - state.mkAttrs(v, 2); + auto attrs = state.buildBindings(2); - Value * vRight = state.allocAttr(v, state.sRight); + auto & vRight = attrs.alloc(state.sRight); auto rsize = right.size(); - state.mkList(*vRight, rsize); + state.mkList(vRight, rsize); if (rsize) - memcpy(vRight->listElems(), right.data(), sizeof(Value *) * rsize); + memcpy(vRight.listElems(), right.data(), sizeof(Value *) * rsize); - Value * vWrong = state.allocAttr(v, state.sWrong); + auto & vWrong = attrs.alloc(state.sWrong); auto wsize = wrong.size(); - state.mkList(*vWrong, wsize); + state.mkList(vWrong, wsize); if (wsize) - memcpy(vWrong->listElems(), wrong.data(), sizeof(Value *) * wsize); + memcpy(vWrong.listElems(), wrong.data(), sizeof(Value *) * wsize); - v.attrs->sort(); + v.mkAttrs(attrs); } static RegisterPrimOp primop_partition({ @@ -3734,10 +3729,10 @@ static void prim_parseDrvName(EvalState & state, const Pos & pos, Value * * args { string name = state.forceStringNoCtx(*args[0], pos); DrvName parsed(name); - state.mkAttrs(v, 2); - mkString(*state.allocAttr(v, state.sName), parsed.name); - mkString(*state.allocAttr(v, state.symbols.create("version")), parsed.version); - v.attrs->sort(); + auto attrs = state.buildBindings(2); + attrs.alloc(state.sName).mkString(parsed.name); + attrs.alloc("version").mkString(parsed.version); + v.mkAttrs(attrs); } static RegisterPrimOp primop_parseDrvName({ @@ -3883,11 +3878,10 @@ void EvalState::createBaseEnv() mkList(v, searchPath.size()); int n = 0; for (auto & i : searchPath) { - auto v2 = v.listElems()[n++] = allocValue(); - mkAttrs(*v2, 2); - mkString(*allocAttr(*v2, symbols.create("path")), i.second); - mkString(*allocAttr(*v2, symbols.create("prefix")), i.first); - v2->attrs->sort(); + auto attrs = buildBindings(2); + attrs.alloc("path").mkString(i.second); + attrs.alloc("prefix").mkString(i.first); + (v.listElems()[n++] = allocValue())->mkAttrs(attrs); } addConstant("__nixPath", v); diff --git a/src/libexpr/primops/context.cc b/src/libexpr/primops/context.cc index 20545afd0..ab08303d8 100644 --- a/src/libexpr/primops/context.cc +++ b/src/libexpr/primops/context.cc @@ -103,27 +103,26 @@ static void prim_getContext(EvalState & state, const Pos & pos, Value * * args, } } - state.mkAttrs(v, contextInfos.size()); + auto attrs = state.buildBindings(contextInfos.size()); auto sPath = state.symbols.create("path"); auto sAllOutputs = state.symbols.create("allOutputs"); for (const auto & info : contextInfos) { - auto & infoVal = *state.allocAttr(v, state.symbols.create(info.first)); - state.mkAttrs(infoVal, 3); + auto infoAttrs = state.buildBindings(3); if (info.second.path) - mkBool(*state.allocAttr(infoVal, sPath), true); + mkBool(infoAttrs.alloc(sPath), true); if (info.second.allOutputs) - mkBool(*state.allocAttr(infoVal, sAllOutputs), true); + mkBool(infoAttrs.alloc(sAllOutputs), true); if (!info.second.outputs.empty()) { - auto & outputsVal = *state.allocAttr(infoVal, state.sOutputs); + auto & outputsVal = infoAttrs.alloc(state.sOutputs); state.mkList(outputsVal, info.second.outputs.size()); - size_t i = 0; - for (const auto & output : info.second.outputs) - mkString(*(outputsVal.listElems()[i++] = state.allocValue()), output); + for (const auto & [i, output] : enumerate(info.second.outputs)) + (outputsVal.listElems()[i] = state.allocValue())->mkString(output); } - infoVal.attrs->sort(); + attrs.alloc(info.first).mkAttrs(infoAttrs); } - v.attrs->sort(); + + v.mkAttrs(attrs); } static RegisterPrimOp primop_getContext("__getContext", 1, prim_getContext); diff --git a/src/libexpr/primops/fetchMercurial.cc b/src/libexpr/primops/fetchMercurial.cc index c23480853..42214c207 100644 --- a/src/libexpr/primops/fetchMercurial.cc +++ b/src/libexpr/primops/fetchMercurial.cc @@ -70,19 +70,19 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar // FIXME: use name auto [tree, input2] = input.fetch(state.store); - state.mkAttrs(v, 8); + auto attrs2 = state.buildBindings(8); auto storePath = state.store->printStorePath(tree.storePath); - mkString(*state.allocAttr(v, state.sOutPath), storePath, PathSet({storePath})); + attrs2.alloc(state.sOutPath).mkString(storePath, {storePath}); if (input2.getRef()) - mkString(*state.allocAttr(v, state.symbols.create("branch")), *input2.getRef()); + attrs2.alloc("branch").mkString(*input2.getRef()); // Backward compatibility: set 'rev' to // 0000000000000000000000000000000000000000 for a dirty tree. auto rev2 = input2.getRev().value_or(Hash(htSHA1)); - mkString(*state.allocAttr(v, state.symbols.create("rev")), rev2.gitRev()); - mkString(*state.allocAttr(v, state.symbols.create("shortRev")), std::string(rev2.gitRev(), 0, 12)); + attrs2.alloc("rev").mkString(rev2.gitRev()); + attrs2.alloc("shortRev").mkString(rev2.gitRev().substr(0, 12)); if (auto revCount = input2.getRevCount()) - mkInt(*state.allocAttr(v, state.symbols.create("revCount")), *revCount); - v.attrs->sort(); + attrs2.alloc("revCount").mkInt(*revCount); + v.mkAttrs(attrs2); state.allowPath(tree.storePath); } diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index 079513873..a97bfcae7 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -21,49 +21,48 @@ void emitTreeAttrs( { assert(input.isImmutable()); - state.mkAttrs(v, 8); + auto attrs = state.buildBindings(8); auto storePath = state.store->printStorePath(tree.storePath); - mkString(*state.allocAttr(v, state.sOutPath), storePath, PathSet({storePath})); + attrs.alloc(state.sOutPath).mkString(storePath, {storePath}); // FIXME: support arbitrary input attributes. auto narHash = input.getNarHash(); assert(narHash); - mkString(*state.allocAttr(v, state.symbols.create("narHash")), - narHash->to_string(SRI, true)); + attrs.alloc("narHash").mkString(narHash->to_string(SRI, true)); if (input.getType() == "git") - mkBool(*state.allocAttr(v, state.symbols.create("submodules")), + attrs.alloc("submodules").mkBool( fetchers::maybeGetBoolAttr(input.attrs, "submodules").value_or(false)); if (!forceDirty) { if (auto rev = input.getRev()) { - mkString(*state.allocAttr(v, state.symbols.create("rev")), rev->gitRev()); - mkString(*state.allocAttr(v, state.symbols.create("shortRev")), rev->gitShortRev()); + attrs.alloc("rev").mkString(rev->gitRev()); + attrs.alloc("shortRev").mkString(rev->gitShortRev()); } else if (emptyRevFallback) { // Backwards compat for `builtins.fetchGit`: dirty repos return an empty sha1 as rev auto emptyHash = Hash(htSHA1); - mkString(*state.allocAttr(v, state.symbols.create("rev")), emptyHash.gitRev()); - mkString(*state.allocAttr(v, state.symbols.create("shortRev")), emptyHash.gitShortRev()); + attrs.alloc("rev").mkString(emptyHash.gitRev()); + attrs.alloc("shortRev").mkString(emptyHash.gitShortRev()); } if (auto revCount = input.getRevCount()) - mkInt(*state.allocAttr(v, state.symbols.create("revCount")), *revCount); + attrs.alloc("revCount").mkInt(*revCount); else if (emptyRevFallback) - mkInt(*state.allocAttr(v, state.symbols.create("revCount")), 0); + attrs.alloc("revCount").mkInt(0); } if (auto lastModified = input.getLastModified()) { - mkInt(*state.allocAttr(v, state.symbols.create("lastModified")), *lastModified); - mkString(*state.allocAttr(v, state.symbols.create("lastModifiedDate")), + attrs.alloc("lastModified").mkInt(*lastModified); + attrs.alloc("lastModifiedDate").mkString( fmt("%s", std::put_time(std::gmtime(&*lastModified), "%Y%m%d%H%M%S"))); } - v.attrs->sort(); + v.mkAttrs(attrs); } std::string fixURI(std::string uri, EvalState & state, const std::string & defaultScheme = "file") diff --git a/src/libexpr/primops/fromTOML.cc b/src/libexpr/primops/fromTOML.cc index 30466fc5b..a328b6736 100644 --- a/src/libexpr/primops/fromTOML.cc +++ b/src/libexpr/primops/fromTOML.cc @@ -24,15 +24,12 @@ static void prim_fromTOML(EvalState & state, const Pos & pos, Value * * args, Va size_t size = 0; for (auto & i : table) { (void) i; size++; } - state.mkAttrs(v, size); + auto attrs = state.buildBindings(size); - for(auto & elem: table) { + for(auto & elem : table) + visit(attrs.alloc(elem.first), elem.second); - auto & v2 = *state.allocAttr(v, state.symbols.create(elem.first)); - visit(v2, elem.second); - } - - v.attrs->sort(); + v.mkAttrs(attrs); } break;; case toml::value_t::array: diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh index 6b4f3c0ae..a8fea02dc 100644 --- a/src/libexpr/value.hh +++ b/src/libexpr/value.hh @@ -10,6 +10,8 @@ namespace nix { +class BindingsBuilder; + typedef enum { tInt = 1, @@ -235,6 +237,10 @@ public: string.context = context; } + void mkString(std::string_view s); + + void mkString(std::string_view s, const PathSet & context); + inline void mkPath(const char * s) { clearValue(); @@ -255,6 +261,8 @@ public: attrs = a; } + Value & mkAttrs(BindingsBuilder & bindings); + inline void mkList(size_t size) { clearValue(); -- cgit v1.2.3 From cc08364315437bfd870363220373c48f64862e83 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 4 Jan 2022 18:24:42 +0100 Subject: Remove non-method mkString() --- src/libexpr/common-eval-args.cc | 2 +- src/libexpr/eval.cc | 13 -------- src/libexpr/eval.hh | 2 -- src/libexpr/flake/flake.cc | 4 +-- src/libexpr/json-to-value.cc | 5 ++- src/libexpr/primops.cc | 66 ++++++++++++++++++---------------------- src/libexpr/primops/context.cc | 7 ++--- src/libexpr/primops/fetchTree.cc | 2 +- src/libexpr/primops/fromTOML.cc | 2 +- src/libexpr/value.hh | 3 -- 10 files changed, 42 insertions(+), 64 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/common-eval-args.cc b/src/libexpr/common-eval-args.cc index ecae2ca1f..fffca4ac5 100644 --- a/src/libexpr/common-eval-args.cc +++ b/src/libexpr/common-eval-args.cc @@ -79,7 +79,7 @@ Bindings * MixEvalArgs::getAutoArgs(EvalState & state) if (i.second[0] == 'E') state.mkThunk_(*v, state.parseExprFromString(string(i.second, 1), absPath("."))); else - mkString(*v, string(i.second, 1)); + v->mkString(((std::string_view) i.second).substr(1)); res.insert(state.symbols.create(i.first), v); } return res.finish(); diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 0710ce270..2ac872745 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -766,25 +766,12 @@ LocalNoInline(void addErrorTrace(Error & e, const Pos & pos, const char * s, con } -void mkString(Value & v, const char * s) -{ - v.mkString(dupString(s)); -} - - void Value::mkString(std::string_view s) { mkString(dupStringWithLen(s.data(), s.size())); } -Value & mkString(Value & v, std::string_view s, const PathSet & context) -{ - v.mkString(s, context); - return v; -} - - void Value::mkString(std::string_view s, const PathSet & context) { mkString(s); diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 44ccc6593..c7f74d7b7 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -44,8 +44,6 @@ struct Env }; -Value & mkString(Value & v, std::string_view s, const PathSet & context = PathSet()); - void copyContext(const Value & v, PathSet & context); diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc index c549c5971..15e8d4b06 100644 --- a/src/libexpr/flake/flake.cc +++ b/src/libexpr/flake/flake.cc @@ -682,7 +682,7 @@ void callFlake(EvalState & state, auto vTmp1 = state.allocValue(); auto vTmp2 = state.allocValue(); - mkString(*vLocks, lockedFlake.lockFile.to_string()); + vLocks->mkString(lockedFlake.lockFile.to_string()); emitTreeAttrs( state, @@ -692,7 +692,7 @@ void callFlake(EvalState & state, false, lockedFlake.flake.forceDirty); - mkString(*vRootSubdir, lockedFlake.flake.lockedRef.subdir); + vRootSubdir->mkString(lockedFlake.flake.lockedRef.subdir); if (!state.vCallFlake) { state.vCallFlake = allocRootValue(state.allocValue()); diff --git a/src/libexpr/json-to-value.cc b/src/libexpr/json-to-value.cc index 9ca5ac86d..3825ca9a9 100644 --- a/src/libexpr/json-to-value.cc +++ b/src/libexpr/json-to-value.cc @@ -113,8 +113,11 @@ public: bool string(string_t & val) { - return handle_value(mkString, val.c_str()); + rs->value(state).mkString(val); + rs->add(); + return true; } + #if NLOHMANN_JSON_VERSION_MAJOR >= 3 && NLOHMANN_JSON_VERSION_MINOR >= 8 bool binary(binary_t&) { diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index daf5fc927..cb18fe494 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1285,7 +1285,7 @@ static RegisterPrimOp primop_derivationStrict(RegisterPrimOp::Info { ‘out’. */ static void prim_placeholder(EvalState & state, const Pos & pos, Value * * args, Value & v) { - mkString(v, hashPlaceholder(state.forceStringNoCtx(*args[0], pos))); + v.mkString(hashPlaceholder(state.forceStringNoCtx(*args[0], pos))); } static RegisterPrimOp primop_placeholder({ @@ -1310,7 +1310,7 @@ static void prim_toPath(EvalState & state, const Pos & pos, Value * * args, Valu { PathSet context; Path path = state.coerceToPath(pos, *args[0], context); - mkString(v, canonPath(path), context); + v.mkString(canonPath(path), context); } static RegisterPrimOp primop_toPath({ @@ -1354,7 +1354,7 @@ static void prim_storePath(EvalState & state, const Pos & pos, Value * * args, V if (!settings.readOnlyMode) state.store->ensurePath(path2); context.insert(state.store->printStorePath(path2)); - mkString(v, path, context); + v.mkString(path, context); } static RegisterPrimOp primop_storePath({ @@ -1420,7 +1420,7 @@ static RegisterPrimOp primop_pathExists({ static void prim_baseNameOf(EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; - mkString(v, baseNameOf(state.coerceToString(pos, *args[0], context, false, false)), context); + v.mkString(baseNameOf(state.coerceToString(pos, *args[0], context, false, false)), context); } static RegisterPrimOp primop_baseNameOf({ @@ -1441,7 +1441,7 @@ static void prim_dirOf(EvalState & state, const Pos & pos, Value * * args, Value { PathSet context; Path dir = dirOf(state.coerceToString(pos, *args[0], context, false, false)); - if (args[0]->type() == nPath) mkPath(v, dir.c_str()); else mkString(v, dir, context); + if (args[0]->type() == nPath) mkPath(v, dir.c_str()); else v.mkString(dir, context); } static RegisterPrimOp primop_dirOf({ @@ -1470,7 +1470,7 @@ static void prim_readFile(EvalState & state, const Pos & pos, Value * * args, Va string s = readFile(path); if (s.find((char) 0) != string::npos) throw Error("the contents of the file '%1%' cannot be represented as a Nix string", path); - mkString(v, s.c_str()); + v.mkString(s); } static RegisterPrimOp primop_readFile({ @@ -1549,7 +1549,7 @@ static void prim_hashFile(EvalState & state, const Pos & pos, Value * * args, Va throw EvalError("cannot read '%s' since path '%s' is not valid, at %s", path, e.path, pos); } - mkString(v, hashFile(*ht, path).to_string(Base16, false)); + v.mkString(hashFile(*ht, path).to_string(Base16, false)); } static RegisterPrimOp primop_hashFile({ @@ -1626,7 +1626,7 @@ static void prim_toXML(EvalState & state, const Pos & pos, Value * * args, Value std::ostringstream out; PathSet context; printValueAsXML(state, true, false, *args[0], out, context, pos); - mkString(v, out.str(), context); + v.mkString(out.str(), context); } static RegisterPrimOp primop_toXML({ @@ -1734,7 +1734,7 @@ static void prim_toJSON(EvalState & state, const Pos & pos, Value * * args, Valu std::ostringstream out; PathSet context; printValueAsJSON(state, true, *args[0], pos, out, context); - mkString(v, out.str(), context); + v.mkString(out.str(), context); } static RegisterPrimOp primop_toJSON({ @@ -1808,7 +1808,7 @@ static void prim_toFile(EvalState & state, const Pos & pos, Value * * args, Valu result, since `storePath' itself has references to the paths used in args[1]. */ - mkString(v, storePath, {storePath}); + v.mkString(storePath, {storePath}); } static RegisterPrimOp primop_toFile({ @@ -1925,10 +1925,10 @@ static void addPath( /* Call the filter function. The first argument is the path, the second is a string indicating the type of the file. */ Value arg1; - mkString(arg1, path); + arg1.mkString(path); Value arg2; - mkString(arg2, + arg2.mkString( S_ISREG(st.st_mode) ? "regular" : S_ISDIR(st.st_mode) ? "directory" : S_ISLNK(st.st_mode) ? "symlink" : @@ -1955,7 +1955,7 @@ static void addPath( } else dstPath = state.store->printStorePath(*expectedStorePath); - mkString(v, dstPath, {dstPath}); + v.mkString(dstPath, {dstPath}); state.allowPath(dstPath); @@ -3303,7 +3303,7 @@ static void prim_toString(EvalState & state, const Pos & pos, Value * * args, Va { PathSet context; string s = state.coerceToString(pos, *args[0], context, true, false); - mkString(v, s, context); + v.mkString(s, context); } static RegisterPrimOp primop_toString({ @@ -3347,7 +3347,7 @@ static void prim_substring(EvalState & state, const Pos & pos, Value * * args, V .errPos = pos }); - mkString(v, (unsigned int) start >= s.size() ? "" : string(s, start, len), context); + v.mkString((unsigned int) start >= s.size() ? "" : string(s, start, len), context); } static RegisterPrimOp primop_substring({ @@ -3401,7 +3401,7 @@ static void prim_hashString(EvalState & state, const Pos & pos, Value * * args, PathSet context; // discarded string s = state.forceString(*args[1], context, pos); - mkString(v, hashString(*ht, s).to_string(Base16, false)); + v.mkString(hashString(*ht, s).to_string(Base16, false)); } static RegisterPrimOp primop_hashString({ @@ -3451,7 +3451,7 @@ void prim_match(EvalState & state, const Pos & pos, Value * * args, Value & v) if (!match[i+1].matched) mkNull(*(v.listElems()[i] = state.allocValue())); else - mkString(*(v.listElems()[i] = state.allocValue()), match[i + 1].str().c_str()); + (v.listElems()[i] = state.allocValue())->mkString(match[i + 1].str()); } } catch (std::regex_error &e) { @@ -3526,7 +3526,6 @@ static void prim_split(EvalState & state, const Pos & pos, Value * * args, Value const size_t len = std::distance(begin, end); state.mkList(v, 2 * len + 1); size_t idx = 0; - Value * elem; if (len == 0) { v.listElems()[idx++] = args[1]; @@ -3538,12 +3537,11 @@ static void prim_split(EvalState & state, const Pos & pos, Value * * args, Value std::smatch match = *i; // Add a string for non-matched characters. - elem = v.listElems()[idx++] = state.allocValue(); - mkString(*elem, match.prefix().str().c_str()); + (v.listElems()[idx++] = state.allocValue())->mkString(match.prefix().str()); // Add a list for matched substrings. const size_t slen = match.size() - 1; - elem = v.listElems()[idx++] = state.allocValue(); + auto elem = v.listElems()[idx++] = state.allocValue(); // Start at 1, beacause the first match is the whole string. state.mkList(*elem, slen); @@ -3551,15 +3549,14 @@ static void prim_split(EvalState & state, const Pos & pos, Value * * args, Value if (!match[si + 1].matched) mkNull(*(elem->listElems()[si] = state.allocValue())); else - mkString(*(elem->listElems()[si] = state.allocValue()), match[si + 1].str().c_str()); + (elem->listElems()[si] = state.allocValue())->mkString(match[si + 1].str()); } // Add a string for non-matched suffix characters. - if (idx == 2 * len) { - elem = v.listElems()[idx++] = state.allocValue(); - mkString(*elem, match.suffix().str().c_str()); - } + if (idx == 2 * len) + (v.listElems()[idx++] = state.allocValue())->mkString(match.suffix().str()); } + assert(idx == 2 * len + 1); } catch (std::regex_error &e) { @@ -3631,7 +3628,7 @@ static void prim_concatStringsSep(EvalState & state, const Pos & pos, Value * * res += state.coerceToString(pos, *elem, context); } - mkString(v, res, context); + v.mkString(res, context); } static RegisterPrimOp primop_concatStringsSep({ @@ -3700,7 +3697,7 @@ static void prim_replaceStrings(EvalState & state, const Pos & pos, Value * * ar } } - mkString(v, res, context); + v.mkString(res, context); } static RegisterPrimOp primop_replaceStrings({ @@ -3781,11 +3778,8 @@ static void prim_splitVersion(EvalState & state, const Pos & pos, Value * * args components.emplace_back(std::move(component)); } state.mkList(v, components.size()); - unsigned int n = 0; - for (auto & component : components) { - auto listElem = v.listElems()[n++] = state.allocValue(); - mkString(*listElem, std::move(component)); - } + for (const auto & [n, component] : enumerate(components)) + (v.listElems()[n] = state.allocValue())->mkString(std::move(component)); } static RegisterPrimOp primop_splitVersion({ @@ -3851,14 +3845,14 @@ void EvalState::createBaseEnv() mkInt(v, time(0)); addConstant("__currentTime", v); - mkString(v, settings.thisSystem.get()); + v.mkString(settings.thisSystem.get()); addConstant("__currentSystem", v); } - mkString(v, nixVersion); + v.mkString(nixVersion); addConstant("__nixVersion", v); - mkString(v, store->storeDir); + v.mkString(store->storeDir); addConstant("__storeDir", v); /* Language version. This should be increased every time a new diff --git a/src/libexpr/primops/context.cc b/src/libexpr/primops/context.cc index ab08303d8..321c3c301 100644 --- a/src/libexpr/primops/context.cc +++ b/src/libexpr/primops/context.cc @@ -7,8 +7,7 @@ namespace nix { static void prim_unsafeDiscardStringContext(EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; - string s = state.coerceToString(pos, *args[0], context); - mkString(v, s, PathSet()); + v.mkString(state.coerceToString(pos, *args[0], context)); } static RegisterPrimOp primop_unsafeDiscardStringContext("__unsafeDiscardStringContext", 1, prim_unsafeDiscardStringContext); @@ -39,7 +38,7 @@ static void prim_unsafeDiscardOutputDependency(EvalState & state, const Pos & po for (auto & p : context) context2.insert(p.at(0) == '=' ? string(p, 1) : p); - mkString(v, s, context2); + v.mkString(s, context2); } static RegisterPrimOp primop_unsafeDiscardOutputDependency("__unsafeDiscardOutputDependency", 1, prim_unsafeDiscardOutputDependency); @@ -186,7 +185,7 @@ static void prim_appendContext(EvalState & state, const Pos & pos, Value * * arg } } - mkString(v, orig, context); + v.mkString(orig, context); } static RegisterPrimOp primop_appendContext("__appendContext", 2, prim_appendContext); diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index a97bfcae7..6647bd35c 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -247,7 +247,7 @@ static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v, state.allowPath(storePath); auto path = state.store->printStorePath(storePath); - mkString(v, path, PathSet({path})); + v.mkString(path, PathSet({path})); } static void prim_fetchurl(EvalState & state, const Pos & pos, Value * * args, Value & v) diff --git a/src/libexpr/primops/fromTOML.cc b/src/libexpr/primops/fromTOML.cc index a328b6736..bae0189ab 100644 --- a/src/libexpr/primops/fromTOML.cc +++ b/src/libexpr/primops/fromTOML.cc @@ -52,7 +52,7 @@ static void prim_fromTOML(EvalState & state, const Pos & pos, Value * * args, Va mkFloat(v, toml::get(t)); break;; case toml::value_t::string: - mkString(v, toml::get(t)); + v.mkString(toml::get(t)); break;; case toml::value_t::local_datetime: case toml::value_t::offset_datetime: diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh index a8fea02dc..94a17136f 100644 --- a/src/libexpr/value.hh +++ b/src/libexpr/value.hh @@ -424,9 +424,6 @@ static inline void mkString(Value & v, const Symbol & s) } -void mkString(Value & v, const char * s); - - void mkPath(Value & v, const char * s); -- cgit v1.2.3 From 263a8d293c67488065296737cdadee3d0e122bf5 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 4 Jan 2022 18:40:39 +0100 Subject: Remove non-method mk functions --- src/libexpr/eval.cc | 26 ++++----- src/libexpr/json-to-value.cc | 27 +++++---- src/libexpr/nixexpr.hh | 4 +- src/libexpr/primops.cc | 118 ++++++++++++++++++++-------------------- src/libexpr/primops/context.cc | 6 +- src/libexpr/primops/fromTOML.cc | 8 +-- src/libexpr/value.hh | 25 --------- 7 files changed, 96 insertions(+), 118 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 2ac872745..591aa6583 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -613,7 +613,7 @@ Value * EvalState::addPrimOp(const string & name, auto vPrimOp = allocValue(); vPrimOp->mkPrimOp(new PrimOp { .fun = primOp, .arity = 1, .name = sym }); Value v; - mkApp(v, *vPrimOp, *vPrimOp); + v.mkApp(vPrimOp, vPrimOp); return addConstant(name, v); } @@ -635,7 +635,7 @@ Value * EvalState::addPrimOp(PrimOp && primOp) auto vPrimOp = allocValue(); vPrimOp->mkPrimOp(new PrimOp(std::move(primOp))); Value v; - mkApp(v, *vPrimOp, *vPrimOp); + v.mkApp(vPrimOp, vPrimOp); return addConstant(primOp.name, v); } @@ -887,7 +887,7 @@ void EvalState::mkPos(Value & v, ptr pos) attrs.alloc(sColumn).mkInt(pos->column); v.mkAttrs(attrs); } else - mkNull(v); + v.mkNull(); } @@ -1258,14 +1258,14 @@ void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v) if (vAttrs->type() != nAttrs || (j = vAttrs->attrs->find(name)) == vAttrs->attrs->end()) { - mkBool(v, false); + v.mkBool(false); return; } else { vAttrs = j->value; } } - mkBool(v, true); + v.mkBool(true); } @@ -1544,7 +1544,7 @@ void ExprAssert::eval(EvalState & state, Env & env, Value & v) void ExprOpNot::eval(EvalState & state, Env & env, Value & v) { - mkBool(v, !state.evalBool(env, e)); + v.mkBool(!state.evalBool(env, e)); } @@ -1552,7 +1552,7 @@ void ExprOpEq::eval(EvalState & state, Env & env, Value & v) { Value v1; e1->eval(state, env, v1); Value v2; e2->eval(state, env, v2); - mkBool(v, state.eqValues(v1, v2)); + v.mkBool(state.eqValues(v1, v2)); } @@ -1560,25 +1560,25 @@ void ExprOpNEq::eval(EvalState & state, Env & env, Value & v) { Value v1; e1->eval(state, env, v1); Value v2; e2->eval(state, env, v2); - mkBool(v, !state.eqValues(v1, v2)); + v.mkBool(!state.eqValues(v1, v2)); } void ExprOpAnd::eval(EvalState & state, Env & env, Value & v) { - mkBool(v, state.evalBool(env, e1, pos) && state.evalBool(env, e2, pos)); + v.mkBool(state.evalBool(env, e1, pos) && state.evalBool(env, e2, pos)); } void ExprOpOr::eval(EvalState & state, Env & env, Value & v) { - mkBool(v, state.evalBool(env, e1, pos) || state.evalBool(env, e2, pos)); + v.mkBool(state.evalBool(env, e1, pos) || state.evalBool(env, e2, pos)); } void ExprOpImpl::eval(EvalState & state, Env & env, Value & v) { - mkBool(v, !state.evalBool(env, e1, pos) || state.evalBool(env, e2, pos)); + v.mkBool(!state.evalBool(env, e1, pos) || state.evalBool(env, e2, pos)); } @@ -1705,9 +1705,9 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v) } if (firstType == nInt) - mkInt(v, n); + v.mkInt(n); else if (firstType == nFloat) - mkFloat(v, nf); + v.mkFloat(nf); else if (firstType == nPath) { if (!context.empty()) throwEvalError(pos, "a string that refers to a store path cannot be appended to a path"); diff --git a/src/libexpr/json-to-value.cc b/src/libexpr/json-to-value.cc index 3825ca9a9..4dc41729a 100644 --- a/src/libexpr/json-to-value.cc +++ b/src/libexpr/json-to-value.cc @@ -76,39 +76,42 @@ class JSONSax : nlohmann::json_sax { EvalState & state; std::unique_ptr rs; - template inline bool handle_value(T f, Args... args) - { - f(rs->value(state), args...); - rs->add(); - return true; - } - public: JSONSax(EvalState & state, Value & v) : state(state), rs(new JSONState(&v)) {}; bool null() { - return handle_value(mkNull); + rs->value(state).mkNull(); + rs->add(); + return true; } bool boolean(bool val) { - return handle_value(mkBool, val); + rs->value(state).mkBool(val); + rs->add(); + return true; } bool number_integer(number_integer_t val) { - return handle_value(mkInt, val); + rs->value(state).mkInt(val); + rs->add(); + return true; } bool number_unsigned(number_unsigned_t val) { - return handle_value(mkInt, val); + rs->value(state).mkInt(val); + rs->add(); + return true; } bool number_float(number_float_t val, const string_t & s) { - return handle_value(mkFloat, val); + rs->value(state).mkFloat(val); + rs->add(); + return true; } bool string(string_t & val) diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index b328b3941..aa5a3f332 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -94,7 +94,7 @@ struct ExprInt : Expr { NixInt n; Value v; - ExprInt(NixInt n) : n(n) { mkInt(v, n); }; + ExprInt(NixInt n) : n(n) { v.mkInt(n); }; COMMON_METHODS Value * maybeThunk(EvalState & state, Env & env); }; @@ -103,7 +103,7 @@ struct ExprFloat : Expr { NixFloat nf; Value v; - ExprFloat(NixFloat nf) : nf(nf) { mkFloat(v, nf); }; + ExprFloat(NixFloat nf) : nf(nf) { v.mkFloat(nf); }; COMMON_METHODS Value * maybeThunk(EvalState & state, Env & env); }; diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index cb18fe494..57be5ac23 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -196,7 +196,7 @@ static void import(EvalState & state, const Pos & pos, Value & vPath, Value * vS } state.forceFunction(**state.vImportedDrvToDerivation, pos); - mkApp(v, **state.vImportedDrvToDerivation, *w); + v.mkApp(*state.vImportedDrvToDerivation, w); state.forceAttrs(v, pos); } @@ -430,7 +430,7 @@ static RegisterPrimOp primop_typeOf({ static void prim_isNull(EvalState & state, const Pos & pos, Value * * args, Value & v) { state.forceValue(*args[0], pos); - mkBool(v, args[0]->type() == nNull); + v.mkBool(args[0]->type() == nNull); } static RegisterPrimOp primop_isNull({ @@ -450,7 +450,7 @@ static RegisterPrimOp primop_isNull({ static void prim_isFunction(EvalState & state, const Pos & pos, Value * * args, Value & v) { state.forceValue(*args[0], pos); - mkBool(v, args[0]->type() == nFunction); + v.mkBool(args[0]->type() == nFunction); } static RegisterPrimOp primop_isFunction({ @@ -466,7 +466,7 @@ static RegisterPrimOp primop_isFunction({ static void prim_isInt(EvalState & state, const Pos & pos, Value * * args, Value & v) { state.forceValue(*args[0], pos); - mkBool(v, args[0]->type() == nInt); + v.mkBool(args[0]->type() == nInt); } static RegisterPrimOp primop_isInt({ @@ -482,7 +482,7 @@ static RegisterPrimOp primop_isInt({ static void prim_isFloat(EvalState & state, const Pos & pos, Value * * args, Value & v) { state.forceValue(*args[0], pos); - mkBool(v, args[0]->type() == nFloat); + v.mkBool(args[0]->type() == nFloat); } static RegisterPrimOp primop_isFloat({ @@ -498,7 +498,7 @@ static RegisterPrimOp primop_isFloat({ static void prim_isString(EvalState & state, const Pos & pos, Value * * args, Value & v) { state.forceValue(*args[0], pos); - mkBool(v, args[0]->type() == nString); + v.mkBool(args[0]->type() == nString); } static RegisterPrimOp primop_isString({ @@ -514,7 +514,7 @@ static RegisterPrimOp primop_isString({ static void prim_isBool(EvalState & state, const Pos & pos, Value * * args, Value & v) { state.forceValue(*args[0], pos); - mkBool(v, args[0]->type() == nBool); + v.mkBool(args[0]->type() == nBool); } static RegisterPrimOp primop_isBool({ @@ -530,7 +530,7 @@ static RegisterPrimOp primop_isBool({ static void prim_isPath(EvalState & state, const Pos & pos, Value * * args, Value & v) { state.forceValue(*args[0], pos); - mkBool(v, args[0]->type() == nPath); + v.mkBool(args[0]->type() == nPath); } static RegisterPrimOp primop_isPath({ @@ -685,7 +685,7 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar /* Call the `operator' function with `e' as argument. */ Value call; - mkApp(call, *op->value, *e); + call.mkApp(op->value, e); state.forceList(call, pos); /* Add the values returned by the operator to the work set. */ @@ -761,7 +761,7 @@ static RegisterPrimOp primop_addErrorContext(RegisterPrimOp::Info { static void prim_ceil(EvalState & state, const Pos & pos, Value * * args, Value & v) { auto value = state.forceFloat(*args[0], args[0]->determinePos(pos)); - mkInt(v, ceil(value)); + v.mkInt(ceil(value)); } static RegisterPrimOp primop_ceil({ @@ -780,7 +780,7 @@ static RegisterPrimOp primop_ceil({ static void prim_floor(EvalState & state, const Pos & pos, Value * * args, Value & v) { auto value = state.forceFloat(*args[0], args[0]->determinePos(pos)); - mkInt(v, floor(value)); + v.mkInt(floor(value)); } static RegisterPrimOp primop_floor({ @@ -804,10 +804,10 @@ static void prim_tryEval(EvalState & state, const Pos & pos, Value * * args, Val try { state.forceValue(*args[0], pos); attrs.insert(state.sValue, args[0]); - mkBool(attrs.alloc("success"), true); + attrs.alloc("success").mkBool(true); } catch (AssertionError & e) { - mkBool(attrs.alloc(state.sValue), false); - mkBool(attrs.alloc("success"), false); + attrs.alloc(state.sValue).mkBool(false); + attrs.alloc("success").mkBool(false); } v.mkAttrs(attrs); } @@ -1395,13 +1395,13 @@ static void prim_pathExists(EvalState & state, const Pos & pos, Value * * args, } try { - mkBool(v, pathExists(state.checkSourcePath(path))); + v.mkBool(pathExists(state.checkSourcePath(path))); } catch (SysError & e) { /* Don't give away info from errors while canonicalising ‘path’ in restricted mode. */ - mkBool(v, false); + v.mkBool(false); } catch (RestrictedPathError & e) { - mkBool(v, false); + v.mkBool(false); } } @@ -2213,7 +2213,7 @@ static void prim_unsafeGetAttrPos(EvalState & state, const Pos & pos, Value * * state.forceAttrs(*args[1], pos); Bindings::iterator i = args[1]->attrs->find(state.symbols.create(attr)); if (i == args[1]->attrs->end()) - mkNull(v); + v.mkNull(); else state.mkPos(v, i->pos); } @@ -2229,7 +2229,7 @@ static void prim_hasAttr(EvalState & state, const Pos & pos, Value * * args, Val { string attr = state.forceStringNoCtx(*args[0], pos); state.forceAttrs(*args[1], pos); - mkBool(v, args[1]->attrs->find(state.symbols.create(attr)) != args[1]->attrs->end()); + v.mkBool(args[1]->attrs->find(state.symbols.create(attr)) != args[1]->attrs->end()); } static RegisterPrimOp primop_hasAttr({ @@ -2247,7 +2247,7 @@ static RegisterPrimOp primop_hasAttr({ static void prim_isAttrs(EvalState & state, const Pos & pos, Value * * args, Value & v) { state.forceValue(*args[0], pos); - mkBool(v, args[0]->type() == nAttrs); + v.mkBool(args[0]->type() == nAttrs); } static RegisterPrimOp primop_isAttrs({ @@ -2446,7 +2446,7 @@ static void prim_functionArgs(EvalState & state, const Pos & pos, Value * * args auto attrs = state.buildBindings(args[0]->lambda.fun->formals->formals.size()); for (auto & i : args[0]->lambda.fun->formals->formals) // !!! should optimise booleans (allocate only once) - mkBool(attrs.alloc(i.name, ptr(&i.pos)), i.def); + attrs.alloc(i.name, ptr(&i.pos)).mkBool(i.def); v.mkAttrs(attrs); } @@ -2478,8 +2478,8 @@ static void prim_mapAttrs(EvalState & state, const Pos & pos, Value * * args, Va Value * vName = state.allocValue(); Value * vFun2 = state.allocValue(); mkString(*vName, i.name); - mkApp(*vFun2, *args[0], *vName); - mkApp(*state.allocAttr(v, i.name), *vFun2, *i.value); + vFun2->mkApp(args[0], vName); + state.allocAttr(v, i.name)->mkApp(vFun2, i.value); } } @@ -2542,10 +2542,10 @@ static void prim_zipAttrsWith(EvalState & state, const Pos & pos, Value * * args for (auto & attr : *v.attrs) { Value * name = state.allocValue(); mkString(*name, attr.name); - Value * call1 = state.allocValue(); - mkApp(*call1, *args[0], *name); - Value * call2 = state.allocValue(); - mkApp(*call2, *call1, *attr.value); + auto call1 = state.allocValue(); + call1->mkApp(args[0], name); + auto call2 = state.allocValue(); + call2->mkApp(call1, attr.value); attr.value = call2; } } @@ -2592,7 +2592,7 @@ static RegisterPrimOp primop_zipAttrsWith({ static void prim_isList(EvalState & state, const Pos & pos, Value * * args, Value & v) { state.forceValue(*args[0], pos); - mkBool(v, args[0]->type() == nList); + v.mkBool(args[0]->type() == nList); } static RegisterPrimOp primop_isList({ @@ -2690,8 +2690,8 @@ static void prim_map(EvalState & state, const Pos & pos, Value * * args, Value & state.mkList(v, args[1]->listSize()); for (unsigned int n = 0; n < v.listSize(); ++n) - mkApp(*(v.listElems()[n] = state.allocValue()), - *args[0], *args[1]->listElems()[n]); + (v.listElems()[n] = state.allocValue())->mkApp( + args[0], args[1]->listElems()[n]); } static RegisterPrimOp primop_map({ @@ -2760,7 +2760,7 @@ static void prim_elem(EvalState & state, const Pos & pos, Value * * args, Value res = true; break; } - mkBool(v, res); + v.mkBool(res); } static RegisterPrimOp primop_elem({ @@ -2793,7 +2793,7 @@ static RegisterPrimOp primop_concatLists({ static void prim_length(EvalState & state, const Pos & pos, Value * * args, Value & v) { state.forceList(*args[0], pos); - mkInt(v, args[0]->listSize()); + v.mkInt(args[0]->listSize()); } static RegisterPrimOp primop_length({ @@ -2850,12 +2850,12 @@ static void anyOrAll(bool any, EvalState & state, const Pos & pos, Value * * arg state.callFunction(*args[0], *elem, vTmp, pos); bool res = state.forceBool(vTmp, pos); if (res == any) { - mkBool(v, any); + v.mkBool(any); return; } } - mkBool(v, !any); + v.mkBool(!any); } @@ -2902,9 +2902,9 @@ static void prim_genList(EvalState & state, const Pos & pos, Value * * args, Val state.mkList(v, len); for (unsigned int n = 0; n < (unsigned int) len; ++n) { - Value * arg = state.allocValue(); - mkInt(*arg, n); - mkApp(*(v.listElems()[n] = state.allocValue()), *args[0], *arg); + auto arg = state.allocValue(); + arg->mkInt(n); + (v.listElems()[n] = state.allocValue())->mkApp(args[0], arg); } } @@ -3140,9 +3140,9 @@ static void prim_add(EvalState & state, const Pos & pos, Value * * args, Value & state.forceValue(*args[0], pos); state.forceValue(*args[1], pos); if (args[0]->type() == nFloat || args[1]->type() == nFloat) - mkFloat(v, state.forceFloat(*args[0], pos) + state.forceFloat(*args[1], pos)); + v.mkFloat(state.forceFloat(*args[0], pos) + state.forceFloat(*args[1], pos)); else - mkInt(v, state.forceInt(*args[0], pos) + state.forceInt(*args[1], pos)); + v.mkInt(state.forceInt(*args[0], pos) + state.forceInt(*args[1], pos)); } static RegisterPrimOp primop_add({ @@ -3159,9 +3159,9 @@ static void prim_sub(EvalState & state, const Pos & pos, Value * * args, Value & state.forceValue(*args[0], pos); state.forceValue(*args[1], pos); if (args[0]->type() == nFloat || args[1]->type() == nFloat) - mkFloat(v, state.forceFloat(*args[0], pos) - state.forceFloat(*args[1], pos)); + v.mkFloat(state.forceFloat(*args[0], pos) - state.forceFloat(*args[1], pos)); else - mkInt(v, state.forceInt(*args[0], pos) - state.forceInt(*args[1], pos)); + v.mkInt(state.forceInt(*args[0], pos) - state.forceInt(*args[1], pos)); } static RegisterPrimOp primop_sub({ @@ -3178,9 +3178,9 @@ static void prim_mul(EvalState & state, const Pos & pos, Value * * args, Value & state.forceValue(*args[0], pos); state.forceValue(*args[1], pos); if (args[0]->type() == nFloat || args[1]->type() == nFloat) - mkFloat(v, state.forceFloat(*args[0], pos) * state.forceFloat(*args[1], pos)); + v.mkFloat(state.forceFloat(*args[0], pos) * state.forceFloat(*args[1], pos)); else - mkInt(v, state.forceInt(*args[0], pos) * state.forceInt(*args[1], pos)); + v.mkInt(state.forceInt(*args[0], pos) * state.forceInt(*args[1], pos)); } static RegisterPrimOp primop_mul({ @@ -3205,7 +3205,7 @@ static void prim_div(EvalState & state, const Pos & pos, Value * * args, Value & }); if (args[0]->type() == nFloat || args[1]->type() == nFloat) { - mkFloat(v, state.forceFloat(*args[0], pos) / state.forceFloat(*args[1], pos)); + v.mkFloat(state.forceFloat(*args[0], pos) / state.forceFloat(*args[1], pos)); } else { NixInt i1 = state.forceInt(*args[0], pos); NixInt i2 = state.forceInt(*args[1], pos); @@ -3216,7 +3216,7 @@ static void prim_div(EvalState & state, const Pos & pos, Value * * args, Value & .errPos = pos }); - mkInt(v, i1 / i2); + v.mkInt(i1 / i2); } } @@ -3231,7 +3231,7 @@ static RegisterPrimOp primop_div({ static void prim_bitAnd(EvalState & state, const Pos & pos, Value * * args, Value & v) { - mkInt(v, state.forceInt(*args[0], pos) & state.forceInt(*args[1], pos)); + v.mkInt(state.forceInt(*args[0], pos) & state.forceInt(*args[1], pos)); } static RegisterPrimOp primop_bitAnd({ @@ -3245,7 +3245,7 @@ static RegisterPrimOp primop_bitAnd({ static void prim_bitOr(EvalState & state, const Pos & pos, Value * * args, Value & v) { - mkInt(v, state.forceInt(*args[0], pos) | state.forceInt(*args[1], pos)); + v.mkInt(state.forceInt(*args[0], pos) | state.forceInt(*args[1], pos)); } static RegisterPrimOp primop_bitOr({ @@ -3259,7 +3259,7 @@ static RegisterPrimOp primop_bitOr({ static void prim_bitXor(EvalState & state, const Pos & pos, Value * * args, Value & v) { - mkInt(v, state.forceInt(*args[0], pos) ^ state.forceInt(*args[1], pos)); + v.mkInt(state.forceInt(*args[0], pos) ^ state.forceInt(*args[1], pos)); } static RegisterPrimOp primop_bitXor({ @@ -3276,7 +3276,7 @@ static void prim_lessThan(EvalState & state, const Pos & pos, Value * * args, Va state.forceValue(*args[0], pos); state.forceValue(*args[1], pos); CompareValues comp{state}; - mkBool(v, comp(args[0], args[1])); + v.mkBool(comp(args[0], args[1])); } static RegisterPrimOp primop_lessThan({ @@ -3374,7 +3374,7 @@ static void prim_stringLength(EvalState & state, const Pos & pos, Value * * args { PathSet context; string s = state.coerceToString(pos, *args[0], context); - mkInt(v, s.size()); + v.mkInt(s.size()); } static RegisterPrimOp primop_stringLength({ @@ -3440,7 +3440,7 @@ void prim_match(EvalState & state, const Pos & pos, Value * * args, Value & v) std::smatch match; if (!std::regex_match(str, match, regex->second)) { - mkNull(v); + v.mkNull(); return; } @@ -3449,7 +3449,7 @@ void prim_match(EvalState & state, const Pos & pos, Value * * args, Value & v) state.mkList(v, len); for (size_t i = 0; i < len; ++i) { if (!match[i+1].matched) - mkNull(*(v.listElems()[i] = state.allocValue())); + (v.listElems()[i] = state.allocValue())->mkNull(); else (v.listElems()[i] = state.allocValue())->mkString(match[i + 1].str()); } @@ -3547,7 +3547,7 @@ static void prim_split(EvalState & state, const Pos & pos, Value * * args, Value state.mkList(*elem, slen); for (size_t si = 0; si < slen; ++si) { if (!match[si + 1].matched) - mkNull(*(elem->listElems()[si] = state.allocValue())); + (elem->listElems()[si] = state.allocValue())->mkNull(); else (elem->listElems()[si] = state.allocValue())->mkString(match[si + 1].str()); } @@ -3750,7 +3750,7 @@ static void prim_compareVersions(EvalState & state, const Pos & pos, Value * * a { string version1 = state.forceStringNoCtx(*args[0], pos); string version2 = state.forceStringNoCtx(*args[1], pos); - mkInt(v, compareVersions(version1, version2)); + v.mkInt(compareVersions(version1, version2)); } static RegisterPrimOp primop_compareVersions({ @@ -3832,17 +3832,17 @@ void EvalState::createBaseEnv() mkAttrs(v, 128); addConstant("builtins", v); - mkBool(v, true); + v.mkBool(true); addConstant("true", v); - mkBool(v, false); + v.mkBool(false); addConstant("false", v); - mkNull(v); + v.mkNull(); addConstant("null", v); if (!evalSettings.pureEval) { - mkInt(v, time(0)); + v.mkInt(time(0)); addConstant("__currentTime", v); v.mkString(settings.thisSystem.get()); @@ -3859,7 +3859,7 @@ void EvalState::createBaseEnv() language feature gets added. It's not necessary to increase it when primops get added, because you can just use `builtins ? primOp' to check. */ - mkInt(v, 6); + v.mkInt(6); addConstant("__langVersion", v); // Miscellaneous diff --git a/src/libexpr/primops/context.cc b/src/libexpr/primops/context.cc index 321c3c301..a239c06da 100644 --- a/src/libexpr/primops/context.cc +++ b/src/libexpr/primops/context.cc @@ -17,7 +17,7 @@ static void prim_hasContext(EvalState & state, const Pos & pos, Value * * args, { PathSet context; state.forceString(*args[0], context, pos); - mkBool(v, !context.empty()); + v.mkBool(!context.empty()); } static RegisterPrimOp primop_hasContext("__hasContext", 1, prim_hasContext); @@ -109,9 +109,9 @@ static void prim_getContext(EvalState & state, const Pos & pos, Value * * args, for (const auto & info : contextInfos) { auto infoAttrs = state.buildBindings(3); if (info.second.path) - mkBool(infoAttrs.alloc(sPath), true); + infoAttrs.alloc(sPath).mkBool(true); if (info.second.allOutputs) - mkBool(infoAttrs.alloc(sAllOutputs), true); + infoAttrs.alloc(sAllOutputs).mkBool(true); if (!info.second.outputs.empty()) { auto & outputsVal = infoAttrs.alloc(state.sOutputs); state.mkList(outputsVal, info.second.outputs.size()); diff --git a/src/libexpr/primops/fromTOML.cc b/src/libexpr/primops/fromTOML.cc index bae0189ab..80c7e0b82 100644 --- a/src/libexpr/primops/fromTOML.cc +++ b/src/libexpr/primops/fromTOML.cc @@ -43,13 +43,13 @@ static void prim_fromTOML(EvalState & state, const Pos & pos, Value * * args, Va } break;; case toml::value_t::boolean: - mkBool(v, toml::get(t)); + v.mkBool(toml::get(t)); break;; case toml::value_t::integer: - mkInt(v, toml::get(t)); + v.mkInt(toml::get(t)); break;; case toml::value_t::floating: - mkFloat(v, toml::get(t)); + v.mkFloat(toml::get(t)); break;; case toml::value_t::string: v.mkString(toml::get(t)); @@ -62,7 +62,7 @@ static void prim_fromTOML(EvalState & state, const Pos & pos, Value * * args, Va throw std::runtime_error("Dates and times are not supported"); break;; case toml::value_t::empty: - mkNull(v); + v.mkNull(); break;; } diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh index 94a17136f..3f9b65197 100644 --- a/src/libexpr/value.hh +++ b/src/libexpr/value.hh @@ -393,31 +393,6 @@ public: // TODO: Remove these static functions, replace call sites with v.mk* instead -static inline void mkInt(Value & v, NixInt n) -{ - v.mkInt(n); -} - -static inline void mkFloat(Value & v, NixFloat n) -{ - v.mkFloat(n); -} - -static inline void mkBool(Value & v, bool b) -{ - v.mkBool(b); -} - -static inline void mkNull(Value & v) -{ - v.mkNull(); -} - -static inline void mkApp(Value & v, Value & left, Value & right) -{ - v.mkApp(&left, &right); -} - static inline void mkString(Value & v, const Symbol & s) { v.mkString(((const string &) s).c_str()); -- cgit v1.2.3 From ed93aec3c317e5dc3748b28e451c8eb6df377060 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 4 Jan 2022 18:45:16 +0100 Subject: Remove non-method mkPath() --- src/libexpr/eval.cc | 7 +++---- src/libexpr/primops.cc | 4 ++-- src/libexpr/value.hh | 5 ++--- 3 files changed, 7 insertions(+), 9 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 591aa6583..cd7d19747 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -786,9 +786,9 @@ void Value::mkString(std::string_view s, const PathSet & context) } -void mkPath(Value & v, const char * s) +void Value::mkPath(std::string_view s) { - v.mkPath(dupString(s)); + mkPath(dupStringWithLen(s.data(), s.size())); } @@ -1711,8 +1711,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v) else if (firstType == nPath) { if (!context.empty()) throwEvalError(pos, "a string that refers to a store path cannot be appended to a path"); - auto path = canonPath(s.str()); - mkPath(v, path.c_str()); + v.mkPath(canonPath(s.str())); } else v.mkString(s.str(), context); } diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 57be5ac23..650200ae4 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1441,7 +1441,7 @@ static void prim_dirOf(EvalState & state, const Pos & pos, Value * * args, Value { PathSet context; Path dir = dirOf(state.coerceToString(pos, *args[0], context, false, false)); - if (args[0]->type() == nPath) mkPath(v, dir.c_str()); else v.mkString(dir, context); + if (args[0]->type() == nPath) v.mkPath(dir); else v.mkString(dir, context); } static RegisterPrimOp primop_dirOf({ @@ -1522,7 +1522,7 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va string path = state.forceStringNoCtx(*args[1], pos); - mkPath(v, state.checkSourcePath(state.findFile(searchPath, path, pos)).c_str()); + v.mkPath(state.checkSourcePath(state.findFile(searchPath, path, pos))); } static RegisterPrimOp primop_findFile(RegisterPrimOp::Info { diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh index 3f9b65197..ba647a10d 100644 --- a/src/libexpr/value.hh +++ b/src/libexpr/value.hh @@ -248,6 +248,8 @@ public: path = s; } + void mkPath(std::string_view s); + inline void mkNull() { clearValue(); @@ -399,9 +401,6 @@ static inline void mkString(Value & v, const Symbol & s) } -void mkPath(Value & v, const char * s); - - #if HAVE_BOEHMGC typedef std::vector > ValueVector; typedef std::map, traceable_allocator > > ValueMap; -- cgit v1.2.3 From ca5baf239277daecef489a415fe157094bd7e799 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 4 Jan 2022 19:09:40 +0100 Subject: Turn mkString(Symbol) into a method --- src/libexpr/nixexpr.hh | 2 +- src/libexpr/primops.cc | 10 +++++----- src/libexpr/value.hh | 13 +++++-------- 3 files changed, 11 insertions(+), 14 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index aa5a3f332..0a60057e5 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -112,7 +112,7 @@ struct ExprString : Expr { Symbol s; Value v; - ExprString(const Symbol & s) : s(s) { mkString(v, s); }; + ExprString(const Symbol & s) : s(s) { v.mkString(s); }; COMMON_METHODS Value * maybeThunk(EvalState & state, Env & env); }; diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 650200ae4..879dfa0a3 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -412,7 +412,7 @@ static void prim_typeOf(EvalState & state, const Pos & pos, Value * * args, Valu case nFloat: t = "float"; break; case nThunk: abort(); } - mkString(v, state.symbols.create(t)); + v.mkString(state.symbols.create(t)); } static RegisterPrimOp primop_typeOf({ @@ -2129,7 +2129,7 @@ static void prim_attrNames(EvalState & state, const Pos & pos, Value * * args, V size_t n = 0; for (auto & i : *args[0]->attrs) - mkString(*(v.listElems()[n++] = state.allocValue()), i.name); + (v.listElems()[n++] = state.allocValue())->mkString(i.name); std::sort(v.listElems(), v.listElems() + n, [](Value * v1, Value * v2) { return strcmp(v1->string.s, v2->string.s) < 0; }); @@ -2477,7 +2477,7 @@ static void prim_mapAttrs(EvalState & state, const Pos & pos, Value * * args, Va for (auto & i : *args[1]->attrs) { Value * vName = state.allocValue(); Value * vFun2 = state.allocValue(); - mkString(*vName, i.name); + vName->mkString(i.name); vFun2->mkApp(args[0], vName); state.allocAttr(v, i.name)->mkApp(vFun2, i.value); } @@ -2540,8 +2540,8 @@ static void prim_zipAttrsWith(EvalState & state, const Pos & pos, Value * * args } for (auto & attr : *v.attrs) { - Value * name = state.allocValue(); - mkString(*name, attr.name); + auto name = state.allocValue(); + name->mkString(attr.name); auto call1 = state.allocValue(); call1->mkApp(args[0], name); auto call2 = state.allocValue(); diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh index ba647a10d..7d007ebdc 100644 --- a/src/libexpr/value.hh +++ b/src/libexpr/value.hh @@ -241,6 +241,11 @@ public: void mkString(std::string_view s, const PathSet & context); + inline void mkString(const Symbol & s) + { + mkString(((const std::string &) s).c_str()); + } + inline void mkPath(const char * s) { clearValue(); @@ -393,14 +398,6 @@ public: }; - -// TODO: Remove these static functions, replace call sites with v.mk* instead -static inline void mkString(Value & v, const Symbol & s) -{ - v.mkString(((const string &) s).c_str()); -} - - #if HAVE_BOEHMGC typedef std::vector > ValueVector; typedef std::map, traceable_allocator > > ValueMap; -- cgit v1.2.3 From 17daec0b832205df781f6443292667c9aa617047 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 4 Jan 2022 19:23:11 +0100 Subject: Move empty attrset optimisation --- src/libexpr/attr-set.cc | 8 +++----- src/libexpr/eval.cc | 3 +-- src/libexpr/eval.hh | 2 +- 3 files changed, 5 insertions(+), 8 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/attr-set.cc b/src/libexpr/attr-set.cc index d74243f45..fc971957d 100644 --- a/src/libexpr/attr-set.cc +++ b/src/libexpr/attr-set.cc @@ -12,6 +12,8 @@ namespace nix { structure. */ Bindings * EvalState::allocBindings(size_t capacity) { + if (capacity == 0) + return &emptyBindings; if (capacity > std::numeric_limits::max()) throw Error("attribute set of size %d is too big", capacity); return new (allocBytes(sizeof(Bindings) + sizeof(Attr) * capacity)) Bindings((Bindings::size_t) capacity); @@ -20,10 +22,6 @@ Bindings * EvalState::allocBindings(size_t capacity) void EvalState::mkAttrs(Value & v, size_t capacity) { - if (capacity == 0) { - v = vEmptySet; - return; - } v.mkAttrs(allocBindings(capacity)); nrAttrsets++; nrAttrsInAttrsets += capacity; @@ -63,7 +61,7 @@ Value & BindingsBuilder::alloc(std::string_view name, ptr pos) void Bindings::sort() { - std::sort(begin(), end()); + if (size_) std::sort(begin(), end()); } diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index cd7d19747..112690189 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -413,6 +413,7 @@ EvalState::EvalState( , sSelf(symbols.create("self")) , sEpsilon(symbols.create("")) , repair(NoRepair) + , emptyBindings(0) , store(store) , buildStore(buildStore ? buildStore : store) , regexCache(makeRegexCache()) @@ -454,8 +455,6 @@ EvalState::EvalState( } } - vEmptySet.mkAttrs(allocBindings(0)); - createBaseEnv(); } diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index c7f74d7b7..c0d88201a 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -91,7 +91,7 @@ public: mode. */ std::optional allowedPaths; - Value vEmptySet; + Bindings emptyBindings; /* Store used to materialise .drv files. */ const ref store; -- cgit v1.2.3 From 2b4c94482364c236b03f7f8b61aca7076ab4079d Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 4 Jan 2022 20:29:17 +0100 Subject: Remove EvalState::mkAttrs() --- src/libexpr/attr-set.cc | 13 +++---------- src/libexpr/attr-set.hh | 10 ++++++++-- src/libexpr/eval.cc | 20 +++++++++++--------- src/libexpr/eval.hh | 3 ++- src/libexpr/json-to-value.cc | 6 +++--- src/libexpr/primops.cc | 42 +++++++++++++++++++++++++----------------- 6 files changed, 52 insertions(+), 42 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/attr-set.cc b/src/libexpr/attr-set.cc index fc971957d..52ac47e9b 100644 --- a/src/libexpr/attr-set.cc +++ b/src/libexpr/attr-set.cc @@ -7,6 +7,7 @@ namespace nix { + /* Allocate a new array of attributes for an attribute set with a specific capacity. The space is implicitly reserved after the Bindings structure. */ @@ -16,15 +17,9 @@ Bindings * EvalState::allocBindings(size_t capacity) return &emptyBindings; if (capacity > std::numeric_limits::max()) throw Error("attribute set of size %d is too big", capacity); - return new (allocBytes(sizeof(Bindings) + sizeof(Attr) * capacity)) Bindings((Bindings::size_t) capacity); -} - - -void EvalState::mkAttrs(Value & v, size_t capacity) -{ - v.mkAttrs(allocBindings(capacity)); nrAttrsets++; nrAttrsInAttrsets += capacity; + return new (allocBytes(sizeof(Bindings) + sizeof(Attr) * capacity)) Bindings((Bindings::size_t) capacity); } @@ -67,9 +62,7 @@ void Bindings::sort() Value & Value::mkAttrs(BindingsBuilder & bindings) { - clearValue(); - internalType = tAttrs; - attrs = bindings.finish(); + mkAttrs(bindings.finish()); return *this; } diff --git a/src/libexpr/attr-set.hh b/src/libexpr/attr-set.hh index 903289b69..82c348287 100644 --- a/src/libexpr/attr-set.hh +++ b/src/libexpr/attr-set.hh @@ -118,13 +118,14 @@ public: call finish(), which sorts the bindings. */ class BindingsBuilder { - EvalState & state; Bindings * bindings; public: + EvalState & state; + BindingsBuilder(EvalState & state, Bindings * bindings) - : state(state), bindings(bindings) + : bindings(bindings), state(state) { } void insert(Symbol name, Value * value, ptr pos = ptr(&noPos)) @@ -146,6 +147,11 @@ public: bindings->sort(); return bindings; } + + Bindings * alreadySorted() + { + return bindings; + } }; } diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 112690189..81aa86641 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -145,7 +145,7 @@ void printValue(std::ostream & str, std::set & active, const Valu str << v.fpoint; break; default: - throw Error("invalid value"); + abort(); } active.erase(&v); @@ -1065,8 +1065,8 @@ void ExprPath::eval(EvalState & state, Env & env, Value & v) void ExprAttrs::eval(EvalState & state, Env & env, Value & v) { - state.mkAttrs(v, attrs.size() + dynamicAttrs.size()); - Env *dynamicEnv = &env; + v.mkAttrs(state.buildBindings(attrs.size() + dynamicAttrs.size()).finish()); + auto dynamicEnv = &env; if (recursive) { /* Create a new environment that contains the attributes in @@ -1592,7 +1592,7 @@ void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v) if (v1.attrs->size() == 0) { v = v2; return; } if (v2.attrs->size() == 0) { v = v1; return; } - state.mkAttrs(v, v1.attrs->size() + v2.attrs->size()); + auto attrs = state.buildBindings(v1.attrs->size() + v2.attrs->size()); /* Merge the sets, preferring values from the second set. Make sure to keep the resulting vector in sorted order. */ @@ -1601,17 +1601,19 @@ void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v) while (i != v1.attrs->end() && j != v2.attrs->end()) { if (i->name == j->name) { - v.attrs->push_back(*j); + attrs.insert(*j); ++i; ++j; } else if (i->name < j->name) - v.attrs->push_back(*i++); + attrs.insert(*i++); else - v.attrs->push_back(*j++); + attrs.insert(*j++); } - while (i != v1.attrs->end()) v.attrs->push_back(*i++); - while (j != v2.attrs->end()) v.attrs->push_back(*j++); + while (i != v1.attrs->end()) attrs.insert(*i++); + while (j != v2.attrs->end()) attrs.insert(*j++); + + v.mkAttrs(attrs.alreadySorted()); state.nrOpUpdateValuesCopied += v.attrs->size(); } diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index c0d88201a..89814785e 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -347,7 +347,6 @@ public: } void mkList(Value & v, size_t length); - void mkAttrs(Value & v, size_t capacity); void mkThunk_(Value & v, Expr * expr); void mkPos(Value & v, ptr pos); @@ -400,6 +399,8 @@ private: friend struct ExprSelect; friend void prim_getAttr(EvalState & state, const Pos & pos, Value * * args, Value & v); friend void prim_match(EvalState & state, const Pos & pos, Value * * args, Value & v); + + friend struct Value; }; diff --git a/src/libexpr/json-to-value.cc b/src/libexpr/json-to-value.cc index 4dc41729a..88716250c 100644 --- a/src/libexpr/json-to-value.cc +++ b/src/libexpr/json-to-value.cc @@ -37,10 +37,10 @@ class JSONSax : nlohmann::json_sax { ValueMap attrs; std::unique_ptr resolve(EvalState & state) override { - Value & v = parent->value(state); - state.mkAttrs(v, attrs.size()); + auto attrs2 = state.buildBindings(attrs.size()); for (auto & i : attrs) - v.attrs->push_back(Attr(i.first, i.second)); + attrs2.insert(i.first, i.second); + parent->value(state).mkAttrs(attrs2.alreadySorted()); return std::move(parent); } void add() override { v = nullptr; } diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 879dfa0a3..d7382af6b 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -2274,11 +2274,12 @@ static void prim_removeAttrs(EvalState & state, const Pos & pos, Value * * args, /* Copy all attributes not in that set. Note that we don't need to sort v.attrs because it's a subset of an already sorted vector. */ - state.mkAttrs(v, args[0]->attrs->size()); + auto attrs = state.buildBindings(args[0]->attrs->size()); for (auto & i : *args[0]->attrs) { if (!names.count(i.name)) - v.attrs->push_back(i); + attrs.insert(i); } + v.mkAttrs(attrs.alreadySorted()); } static RegisterPrimOp primop_removeAttrs({ @@ -2369,13 +2370,15 @@ static void prim_intersectAttrs(EvalState & state, const Pos & pos, Value * * ar state.forceAttrs(*args[0], pos); state.forceAttrs(*args[1], pos); - state.mkAttrs(v, std::min(args[0]->attrs->size(), args[1]->attrs->size())); + auto attrs = state.buildBindings(std::min(args[0]->attrs->size(), args[1]->attrs->size())); for (auto & i : *args[0]->attrs) { Bindings::iterator j = args[1]->attrs->find(i.name); if (j != args[1]->attrs->end()) - v.attrs->push_back(*j); + attrs.insert(*j); } + + v.mkAttrs(attrs.alreadySorted()); } static RegisterPrimOp primop_intersectAttrs({ @@ -2429,7 +2432,7 @@ static void prim_functionArgs(EvalState & state, const Pos & pos, Value * * args { state.forceValue(*args[0], pos); if (args[0]->isPrimOpApp() || args[0]->isPrimOp()) { - state.mkAttrs(v, 0); + v.mkAttrs(&state.emptyBindings); return; } if (!args[0]->isLambda()) @@ -2439,7 +2442,7 @@ static void prim_functionArgs(EvalState & state, const Pos & pos, Value * * args }); if (!args[0]->lambda.fun->hasFormals()) { - state.mkAttrs(v, 0); + v.mkAttrs(&state.emptyBindings); return; } @@ -2472,15 +2475,17 @@ static void prim_mapAttrs(EvalState & state, const Pos & pos, Value * * args, Va { state.forceAttrs(*args[1], pos); - state.mkAttrs(v, args[1]->attrs->size()); + auto attrs = state.buildBindings(args[1]->attrs->size()); for (auto & i : *args[1]->attrs) { Value * vName = state.allocValue(); Value * vFun2 = state.allocValue(); vName->mkString(i.name); vFun2->mkApp(args[0], vName); - state.allocAttr(v, i.name)->mkApp(vFun2, i.value); + attrs.alloc(i.name).mkApp(vFun2, i.value); } + + v.mkAttrs(attrs.alreadySorted()); } static RegisterPrimOp primop_mapAttrs({ @@ -2526,12 +2531,13 @@ static void prim_zipAttrsWith(EvalState & state, const Pos & pos, Value * * args } } - state.mkAttrs(v, attrsSeen.size()); + auto attrs = state.buildBindings(attrsSeen.size()); for (auto & [sym, elem] : attrsSeen) { - Value * list = state.allocAttr(v, sym); - state.mkList(*list, elem.first); - elem.second = list->listElems(); + auto & list = attrs.alloc(sym); + state.mkList(list, elem.first); + elem.second = list.listElems(); } + v.mkAttrs(attrs.alreadySorted()); for (unsigned int n = 0; n < listSize; ++n) { Value * vElem = listElems[n]; @@ -3054,14 +3060,16 @@ static void prim_groupBy(EvalState & state, const Pos & pos, Value * * args, Val vector->second.push_back(vElem); } - state.mkAttrs(v, attrs.size()); + auto attrs2 = state.buildBindings(attrs.size()); for (auto & i : attrs) { - Value * list = state.allocAttr(v, i.first); + auto & list = attrs2.alloc(i.first); auto size = i.second.size(); - state.mkList(*list, size); - memcpy(list->listElems(), i.second.data(), sizeof(Value *) * size); + state.mkList(list, size); + memcpy(list.listElems(), i.second.data(), sizeof(Value *) * size); } + + v.mkAttrs(attrs2.alreadySorted()); } static RegisterPrimOp primop_groupBy({ @@ -3829,7 +3837,7 @@ void EvalState::createBaseEnv() Value v; /* `builtins' must be first! */ - mkAttrs(v, 128); + v.mkAttrs(buildBindings(128).finish()); addConstant("builtins", v); v.mkBool(true); -- cgit v1.2.3 From c66865dff1747ecd55915ef8c549dc1a088ff168 Mon Sep 17 00:00:00 2001 From: Nikolay Amiantov Date: Fri, 4 Mar 2016 13:47:19 +0300 Subject: builtins.readFile: Propagate path context Co-authored-by: Shea Levy --- src/libexpr/primops.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src/libexpr') diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index d7382af6b..1d2a7d5d2 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1470,7 +1470,11 @@ static void prim_readFile(EvalState & state, const Pos & pos, Value * * args, Va string s = readFile(path); if (s.find((char) 0) != string::npos) throw Error("the contents of the file '%1%' cannot be represented as a Nix string", path); - v.mkString(s); + auto refs = state.store->isInStore(path) ? + state.store->queryPathInfo(state.store->toStorePath(path).first)->references : + StorePathSet{}; + auto context = state.store->printStorePathSet(refs); + v.mkString(s, context); } static RegisterPrimOp primop_readFile({ -- cgit v1.2.3 From 5838354d342f1cdd09da7099e86123b36ecec409 Mon Sep 17 00:00:00 2001 From: pennae Date: Mon, 27 Dec 2021 02:04:49 +0100 Subject: optimize ExprConcatStrings::eval MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit constructing an ostringstream for non-string concats (like integer addition) is a small constant cost that we can avoid. for string concats we can keep all the string temporaries we get from coerceToString and concatenate them in one go, which saves a lot of intermediate temporaries and copies in ostringstream. we can also avoid copying the concatenated string again by directly allocating it in GC memory and moving ownership of the concatenated string into the target value. saves about 2% on system eval. before: Benchmark 1: nix eval --raw --impure --expr 'with import {}; system' Time (mean ± σ): 2.837 s ± 0.031 s [User: 2.562 s, System: 0.191 s] Range (min … max): 2.796 s … 2.892 s 20 runs after: Benchmark 1: nix eval --raw --impure --expr 'with import {}; system' Time (mean ± σ): 2.790 s ± 0.035 s [User: 2.532 s, System: 0.187 s] Range (min … max): 2.722 s … 2.836 s 20 runs --- src/libexpr/eval.cc | 69 ++++++++++++++++++++++++++++++++++++++++++++-------- src/libexpr/value.hh | 2 ++ 2 files changed, 61 insertions(+), 10 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 81aa86641..61bccd6e2 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -36,6 +36,19 @@ namespace nix { +static char * allocString(size_t size) +{ + char * t; +#if HAVE_BOEHMGC + t = (char *) GC_MALLOC_ATOMIC(size); +#else + t = malloc(size); +#endif + if (!t) throw std::bad_alloc(); + return t; +} + + static char * dupString(const char * s) { char * t; @@ -771,19 +784,30 @@ void Value::mkString(std::string_view s) } -void Value::mkString(std::string_view s, const PathSet & context) +static void copyContextToValue(Value & v, const PathSet & context) { - mkString(s); if (!context.empty()) { size_t n = 0; - string.context = (const char * *) + v.string.context = (const char * *) allocBytes((context.size() + 1) * sizeof(char *)); for (auto & i : context) - string.context[n++] = dupString(i.c_str()); - string.context[n] = 0; + v.string.context[n++] = dupString(i.c_str()); + v.string.context[n] = 0; } } +void Value::mkString(std::string_view s, const PathSet & context) +{ + mkString(s); + copyContextToValue(*this, context); +} + +void Value::mkStringMove(const char * s, const PathSet & context) +{ + mkString(s); + copyContextToValue(*this, context); +} + void Value::mkPath(std::string_view s) { @@ -1660,13 +1684,34 @@ void EvalState::concatLists(Value & v, size_t nrLists, Value * * lists, const Po void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v) { PathSet context; - std::ostringstream s; + std::vector s; + size_t sSize = 0; NixInt n = 0; NixFloat nf = 0; bool first = !forceString; ValueType firstType = nString; + const auto str = [&] { + std::string result; + result.reserve(sSize); + for (const auto & part : s) result += part; + return result; + }; + /* c_str() is not str().c_str() because we want to create a string + Value. allocating a GC'd string directly and moving it into a + Value lets us avoid an allocation and copy. */ + const auto c_str = [&] { + char * result = allocString(sSize + 1); + char * tmp = result; + for (const auto & part : s) { + memcpy(tmp, part.c_str(), part.size()); + tmp += part.size(); + } + *tmp = 0; + return result; + }; + for (auto & [i_pos, i] : *es) { Value vTmp; i->eval(state, env, vTmp); @@ -1696,11 +1741,15 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v) nf += vTmp.fpoint; } else throwEvalError(i_pos, "cannot add %1% to a float", showType(vTmp)); - } else + } else { + if (s.empty()) s.reserve(es->size()); /* skip canonization of first path, which would only be not canonized in the first place if it's coming from a ./${foo} type path */ - s << state.coerceToString(i_pos, vTmp, context, false, firstType == nString, !first); + s.emplace_back( + state.coerceToString(i_pos, vTmp, context, false, firstType == nString, !first)); + sSize += s.back().size(); + } first = false; } @@ -1712,9 +1761,9 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v) else if (firstType == nPath) { if (!context.empty()) throwEvalError(pos, "a string that refers to a store path cannot be appended to a path"); - v.mkPath(canonPath(s.str())); + v.mkPath(canonPath(str())); } else - v.mkString(s.str(), context); + v.mkStringMove(c_str(), context); } diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh index 7d007ebdc..1896c7563 100644 --- a/src/libexpr/value.hh +++ b/src/libexpr/value.hh @@ -241,6 +241,8 @@ public: void mkString(std::string_view s, const PathSet & context); + void mkStringMove(const char * s, const PathSet & context); + inline void mkString(const Symbol & s) { mkString(((const std::string &) s).c_str()); -- cgit v1.2.3 From 1bebb1095a122d4741c4ed9cd9cb19f08753872b Mon Sep 17 00:00:00 2001 From: pennae Date: Wed, 12 Jan 2022 18:08:48 +0100 Subject: cache more often-used symbols for primops there's a few symbols in primops we can create once and pick them out of EvalState afterwards instead of creating them every time we need them. this gives almost 1% speedup to an uncached nix search. --- src/libexpr/eval.cc | 5 +++++ src/libexpr/eval.hh | 3 ++- src/libexpr/primops.cc | 21 ++++++++++----------- 3 files changed, 17 insertions(+), 12 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 61bccd6e2..94ffab175 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -425,6 +425,11 @@ EvalState::EvalState( , sDescription(symbols.create("description")) , sSelf(symbols.create("self")) , sEpsilon(symbols.create("")) + , sStartSet(symbols.create("startSet")) + , sOperator(symbols.create("operator")) + , sKey(symbols.create("key")) + , sPath(symbols.create("path")) + , sPrefix(symbols.create("prefix")) , repair(NoRepair) , emptyBindings(0) , store(store) diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 89814785e..348a1b291 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -80,7 +80,8 @@ public: sContentAddressed, sOutputHash, sOutputHashAlgo, sOutputHashMode, sRecurseForDerivations, - sDescription, sSelf, sEpsilon; + sDescription, sSelf, sEpsilon, sStartSet, sOperator, sKey, sPath, + sPrefix; Symbol sDerivationNix; /* If set, force copying files to the Nix store even if they diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 1d2a7d5d2..7b0e6e7df 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -592,16 +592,16 @@ typedef list ValueList; static Bindings::iterator getAttr( EvalState & state, - string funcName, - string attrName, + std::string_view funcName, + Symbol attrSym, Bindings * attrSet, const Pos & pos) { - Bindings::iterator value = attrSet->find(state.symbols.create(attrName)); + Bindings::iterator value = attrSet->find(attrSym); if (value == attrSet->end()) { hintformat errorMsg = hintfmt( "attribute '%s' missing for call to '%s'", - attrName, + attrSym, funcName ); @@ -635,7 +635,7 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar Bindings::iterator startSet = getAttr( state, "genericClosure", - "startSet", + state.sStartSet, args[0]->attrs, pos ); @@ -650,7 +650,7 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar Bindings::iterator op = getAttr( state, "genericClosure", - "operator", + state.sOperator, args[0]->attrs, pos ); @@ -672,7 +672,7 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar state.forceAttrs(*e, pos); Bindings::iterator key = - e->attrs->find(state.symbols.create("key")); + e->attrs->find(state.sKey); if (key == e->attrs->end()) throw EvalError({ .msg = hintfmt("attribute 'key' required"), @@ -1498,14 +1498,14 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va state.forceAttrs(*v2, pos); string prefix; - Bindings::iterator i = v2->attrs->find(state.symbols.create("prefix")); + Bindings::iterator i = v2->attrs->find(state.sPrefix); if (i != v2->attrs->end()) prefix = state.forceStringNoCtx(*i->value, pos); i = getAttr( state, "findFile", - "path", + state.sPath, v2->attrs, pos ); @@ -2184,11 +2184,10 @@ void prim_getAttr(EvalState & state, const Pos & pos, Value * * args, Value & v) { string attr = state.forceStringNoCtx(*args[0], pos); state.forceAttrs(*args[1], pos); - // !!! Should we create a symbol here or just do a lookup? Bindings::iterator i = getAttr( state, "getAttr", - attr, + state.symbols.create(attr), args[1]->attrs, pos ); -- cgit v1.2.3 From ef45787aae1c91dffce1e95db3d46740b0165319 Mon Sep 17 00:00:00 2001 From: pennae Date: Wed, 12 Jan 2022 18:17:59 +0100 Subject: avoid string copies in attrNames sort comparison symbols can also be cast to string_view, which compares the same but doesn't require a copy of both symbol names on every comparison. --- src/libexpr/primops.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/libexpr') diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 7b0e6e7df..365595c92 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -2163,7 +2163,10 @@ static void prim_attrValues(EvalState & state, const Pos & pos, Value * * args, v.listElems()[n++] = (Value *) &i; std::sort(v.listElems(), v.listElems() + n, - [](Value * v1, Value * v2) { return (string) ((Attr *) v1)->name < (string) ((Attr *) v2)->name; }); + [](Value * v1, Value * v2) { + std::string_view s1 = ((Attr *) v1)->name, s2 = ((Attr *) v2)->name; + return s1 < s2; + }); for (unsigned int i = 0; i < n; ++i) v.listElems()[i] = ((Attr *) v.listElems()[i])->value; -- cgit v1.2.3 From 6401e443a441f58f48e2cbab5286b89ec162835a Mon Sep 17 00:00:00 2001 From: pennae Date: Sun, 2 Jan 2022 00:30:57 +0100 Subject: move strings in derivationStrict the temporary will be discarded anyway, so we can move out of it and save many small allocations and copies. --- src/libexpr/primops.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 365595c92..003e588a4 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1079,10 +1079,10 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * } else { auto s = state.coerceToString(*i->pos, *i->value, context, true); drv.env.emplace(key, s); - if (i->name == state.sBuilder) drv.builder = s; - else if (i->name == state.sSystem) drv.platform = s; - else if (i->name == state.sOutputHash) outputHash = s; - else if (i->name == state.sOutputHashAlgo) outputHashAlgo = s; + if (i->name == state.sBuilder) drv.builder = std::move(s); + else if (i->name == state.sSystem) drv.platform = std::move(s); + else if (i->name == state.sOutputHash) outputHash = std::move(s); + else if (i->name == state.sOutputHashAlgo) outputHashAlgo = std::move(s); else if (i->name == state.sOutputHashMode) handleHashMode(s); else if (i->name == state.sOutputs) handleOutputs(tokenizeString(s)); -- cgit v1.2.3 From 61a9d16d5c1d4088981f7d0ca08655f9155cc015 Mon Sep 17 00:00:00 2001 From: pennae Date: Tue, 21 Dec 2021 09:17:31 +0100 Subject: don't strdup tokens in the lexer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit every stringy token the lexer returns is turned into a Symbol and not used further, so we don't have to strdup. using a string_view is sufficient, but due to limitations of the current parser we have to use a POD type that holds the same information. gives ~2% on system build, 6% on search, 8% on parsing alone # before Benchmark 1: nix search --offline nixpkgs hello Time (mean ± σ): 610.6 ms ± 2.4 ms [User: 602.5 ms, System: 7.8 ms] Range (min … max): 606.6 ms … 617.3 ms 50 runs Benchmark 2: nix eval -f hackage-packages.nix Time (mean ± σ): 430.1 ms ± 1.4 ms [User: 393.1 ms, System: 36.7 ms] Range (min … max): 428.2 ms … 434.2 ms 50 runs Benchmark 3: nix eval --raw --impure --expr 'with import {}; system' Time (mean ± σ): 3.032 s ± 0.005 s [User: 2.808 s, System: 0.223 s] Range (min … max): 3.023 s … 3.041 s 50 runs # after Benchmark 1: nix search --offline nixpkgs hello Time (mean ± σ): 574.7 ms ± 2.8 ms [User: 566.3 ms, System: 8.0 ms] Range (min … max): 569.2 ms … 580.7 ms 50 runs Benchmark 2: nix eval -f hackage-packages.nix Time (mean ± σ): 394.4 ms ± 0.8 ms [User: 361.8 ms, System: 32.3 ms] Range (min … max): 392.7 ms … 395.7 ms 50 runs Benchmark 3: nix eval --raw --impure --expr 'with import {}; system' Time (mean ± σ): 2.976 s ± 0.005 s [User: 2.757 s, System: 0.218 s] Range (min … max): 2.966 s … 2.990 s 50 runs --- src/libexpr/lexer.l | 14 +++++++------- src/libexpr/parser.y | 25 ++++++++++++++++--------- 2 files changed, 23 insertions(+), 16 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/lexer.l b/src/libexpr/lexer.l index c18877e29..70e99d2d2 100644 --- a/src/libexpr/lexer.l +++ b/src/libexpr/lexer.l @@ -139,7 +139,7 @@ or { return OR_KW; } \/\/ { return UPDATE; } \+\+ { return CONCAT; } -{ID} { yylval->id = strdup(yytext); return ID; } +{ID} { yylval->id = {yytext, (size_t) yyleng}; return ID; } {INT} { errno = 0; try { yylval->n = boost::lexical_cast(yytext); @@ -221,14 +221,14 @@ or { return OR_KW; } {PATH_SEG} { POP_STATE(); PUSH_STATE(INPATH_SLASH); - yylval->path = strdup(yytext); + yylval->path = {yytext, (size_t) yyleng}; return PATH; } {HPATH_START} { POP_STATE(); PUSH_STATE(INPATH_SLASH); - yylval->path = strdup(yytext); + yylval->path = {yytext, (size_t) yyleng}; return HPATH; } @@ -237,7 +237,7 @@ or { return OR_KW; } PUSH_STATE(INPATH_SLASH); else PUSH_STATE(INPATH); - yylval->path = strdup(yytext); + yylval->path = {yytext, (size_t) yyleng}; return PATH; } {HPATH} { @@ -245,7 +245,7 @@ or { return OR_KW; } PUSH_STATE(INPATH_SLASH); else PUSH_STATE(INPATH); - yylval->path = strdup(yytext); + yylval->path = {yytext, (size_t) yyleng}; return HPATH; } @@ -280,8 +280,8 @@ or { return OR_KW; } throw ParseError("path has a trailing slash"); } -{SPATH} { yylval->path = strdup(yytext); return SPATH; } -{URI} { yylval->uri = strdup(yytext); return URI; } +{SPATH} { yylval->path = {yytext, (size_t) yyleng}; return SPATH; } +{URI} { yylval->uri = {yytext, (size_t) yyleng}; return URI; } [ \t\r\n]+ /* eat up whitespace */ \#[^\r\n]* /* single-line comments */ diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index f8aaea582..049a149cc 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -273,9 +273,16 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err nix::Formal * formal; nix::NixInt n; nix::NixFloat nf; - const char * id; // !!! -> Symbol - char * path; - char * uri; + // using C a struct allows us to avoid having to define the special + // members that using string_view here would implicitly delete. + struct StringToken { + const char * p; + size_t l; + operator std::string_view() const { return {p, l}; } + }; + StringToken id; // !!! -> Symbol + StringToken path; + StringToken uri; std::vector * attrNames; std::vector > * string_parts; } @@ -397,7 +404,7 @@ expr_select expr_simple : ID { - if (strcmp($1, "__curPos") == 0) + if (strncmp($1.p, "__curPos", $1.l) == 0) $$ = new ExprPos(CUR_POS); else $$ = new ExprVar(CUR_POS, data->symbols.create($1)); @@ -414,7 +421,7 @@ expr_simple $$ = new ExprConcatStrings(CUR_POS, false, $2); } | SPATH { - string path($1 + 1, strlen($1) - 2); + string path($1.p + 1, $1.l - 2); $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__findFile")), {new ExprVar(data->symbols.create("__nixPath")), @@ -460,14 +467,14 @@ string_parts_interpolated path_start : PATH { - Path path(absPath($1, data->basePath)); + Path path(absPath({$1.p, $1.l}, data->basePath)); /* add back in the trailing '/' to the first segment */ - if ($1[strlen($1)-1] == '/' && strlen($1) > 1) + if ($1.p[$1.l-1] == '/' && $1.l > 1) path += "/"; $$ = new ExprPath(path); } | HPATH { - Path path(getHome() + string($1 + 1)); + Path path(getHome() + string($1.p + 1, $1.l - 1)); $$ = new ExprPath(path); } ; @@ -543,7 +550,7 @@ attrpath attr : ID { $$ = $1; } - | OR_KW { $$ = "or"; } + | OR_KW { $$ = {"or", 2}; } ; string_attr -- cgit v1.2.3 From eee0bcee227f6a1b46116efc8915545feb5a2e86 Mon Sep 17 00:00:00 2001 From: pennae Date: Mon, 20 Dec 2021 11:29:14 +0100 Subject: avoid allocations in SymbolTable::create MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit speeds up parsing by ~3%, system builds by a bit more than 1% # before Benchmark 1: nix search --offline nixpkgs hello Time (mean ± σ): 574.7 ms ± 2.8 ms [User: 566.3 ms, System: 8.0 ms] Range (min … max): 569.2 ms … 580.7 ms 50 runs Benchmark 2: nix eval -f ../nixpkgs/pkgs/development/haskell-modules/hackage-packages.nix Time (mean ± σ): 394.4 ms ± 0.8 ms [User: 361.8 ms, System: 32.3 ms] Range (min … max): 392.7 ms … 395.7 ms 50 runs Benchmark 3: nix eval --raw --impure --expr 'with import {}; system' Time (mean ± σ): 2.976 s ± 0.005 s [User: 2.757 s, System: 0.218 s] Range (min … max): 2.966 s … 2.990 s 50 runs # after Benchmark 1: nix search --offline nixpkgs hello Time (mean ± σ): 572.4 ms ± 2.3 ms [User: 563.4 ms, System: 8.6 ms] Range (min … max): 566.9 ms … 579.1 ms 50 runs Benchmark 2: nix eval -f ../nixpkgs/pkgs/development/haskell-modules/hackage-packages.nix Time (mean ± σ): 381.7 ms ± 1.0 ms [User: 348.3 ms, System: 33.1 ms] Range (min … max): 380.2 ms … 387.7 ms 50 runs Benchmark 3: nix eval --raw --impure --expr 'with import {}; system' Time (mean ± σ): 2.936 s ± 0.005 s [User: 2.715 s, System: 0.221 s] Range (min … max): 2.923 s … 2.946 s 50 runs --- src/libexpr/nixexpr.cc | 2 +- src/libexpr/symbol-table.hh | 21 ++++++++++++++------- 2 files changed, 15 insertions(+), 8 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index a75357871..640c44c01 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -473,7 +473,7 @@ string ExprLambda::showNamePos() const size_t SymbolTable::totalSize() const { size_t n = 0; - for (auto & i : symbols) + for (auto & i : store) n += i.size(); return n; } diff --git a/src/libexpr/symbol-table.hh b/src/libexpr/symbol-table.hh index 4eb6dac81..a090ebae5 100644 --- a/src/libexpr/symbol-table.hh +++ b/src/libexpr/symbol-table.hh @@ -1,7 +1,8 @@ #pragma once +#include #include -#include +#include #include "types.hh" @@ -70,15 +71,21 @@ public: class SymbolTable { private: - typedef std::unordered_set Symbols; - Symbols symbols; + std::unordered_map symbols; + std::list store; public: Symbol create(std::string_view s) { - // FIXME: avoid allocation if 's' already exists in the symbol table. - std::pair res = symbols.emplace(std::string(s)); - return Symbol(&*res.first); + // Most symbols are looked up more than once, so we trade off insertion performance + // for lookup performance. + // TODO: could probably be done more efficiently with transparent Hash and Equals + // on the original implementation using unordered_set + auto it = symbols.find(s); + if (it != symbols.end()) return it->second; + + const string & rawSym = store.emplace_back(s); + return symbols.emplace(rawSym, Symbol(&rawSym)).first->second; } size_t size() const @@ -91,7 +98,7 @@ public: template void dump(T callback) { - for (auto & s : symbols) + for (auto & s : store) callback(s); } }; -- cgit v1.2.3 From 34e3bd10e3891afc965a7fb8fdcaacbdc900b2d5 Mon Sep 17 00:00:00 2001 From: pennae Date: Tue, 21 Dec 2021 13:56:57 +0100 Subject: avoid copies of parser input data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit when given a string yacc will copy the entire input to a newly allocated location so that it can add a second terminating NUL byte. since the parser is a very internal thing to EvalState we can ensure that having two terminating NUL bytes is always possible without copying, and have the parser itself merely check that the expected NULs are present. # before Benchmark 1: nix search --offline nixpkgs hello Time (mean ± σ): 572.4 ms ± 2.3 ms [User: 563.4 ms, System: 8.6 ms] Range (min … max): 566.9 ms … 579.1 ms 50 runs Benchmark 2: nix eval -f ../nixpkgs/pkgs/development/haskell-modules/hackage-packages.nix Time (mean ± σ): 381.7 ms ± 1.0 ms [User: 348.3 ms, System: 33.1 ms] Range (min … max): 380.2 ms … 387.7 ms 50 runs Benchmark 3: nix eval --raw --impure --expr 'with import {}; system' Time (mean ± σ): 2.936 s ± 0.005 s [User: 2.715 s, System: 0.221 s] Range (min … max): 2.923 s … 2.946 s 50 runs # after Benchmark 1: nix search --offline nixpkgs hello Time (mean ± σ): 571.7 ms ± 2.4 ms [User: 563.3 ms, System: 8.0 ms] Range (min … max): 566.7 ms … 579.7 ms 50 runs Benchmark 2: nix eval -f ../nixpkgs/pkgs/development/haskell-modules/hackage-packages.nix Time (mean ± σ): 376.6 ms ± 1.0 ms [User: 345.8 ms, System: 30.5 ms] Range (min … max): 374.5 ms … 379.1 ms 50 runs Benchmark 3: nix eval --raw --impure --expr 'with import {}; system' Time (mean ± σ): 2.922 s ± 0.006 s [User: 2.707 s, System: 0.215 s] Range (min … max): 2.906 s … 2.934 s 50 runs --- src/libexpr/eval.hh | 6 +++--- src/libexpr/parser.y | 23 +++++++++++++++-------- src/libexpr/primops.cc | 9 ++++++--- 3 files changed, 24 insertions(+), 14 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index cc63294c6..15925a6b4 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -181,8 +181,8 @@ public: Expr * parseExprFromFile(const Path & path, StaticEnv & staticEnv); /* Parse a Nix expression from the specified string. */ - Expr * parseExprFromString(std::string_view s, const Path & basePath, StaticEnv & staticEnv); - Expr * parseExprFromString(std::string_view s, const Path & basePath); + Expr * parseExprFromString(std::string s, const Path & basePath, StaticEnv & staticEnv); + Expr * parseExprFromString(std::string s, const Path & basePath); Expr * parseStdin(); @@ -310,7 +310,7 @@ private: friend struct ExprAttrs; friend struct ExprLet; - Expr * parse(const char * text, FileOrigin origin, const Path & path, + Expr * parse(char * text, size_t length, FileOrigin origin, const Path & path, const Path & basePath, StaticEnv & staticEnv); public: diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 049a149cc..a3e713937 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -596,7 +596,7 @@ formal namespace nix { -Expr * EvalState::parse(const char * text, FileOrigin origin, +Expr * EvalState::parse(char * text, size_t length, FileOrigin origin, const Path & path, const Path & basePath, StaticEnv & staticEnv) { yyscan_t scanner; @@ -616,7 +616,7 @@ Expr * EvalState::parse(const char * text, FileOrigin origin, data.basePath = basePath; yylex_init(&scanner); - yy_scan_string(text, scanner); + yy_scan_buffer(text, length, scanner); int res = yyparse(scanner, &data); yylex_destroy(scanner); @@ -662,26 +662,33 @@ Expr * EvalState::parseExprFromFile(const Path & path) Expr * EvalState::parseExprFromFile(const Path & path, StaticEnv & staticEnv) { - return parse(readFile(path).c_str(), foFile, path, dirOf(path), staticEnv); + auto buffer = readFile(path); + // readFile should have left some extra space for terminators + buffer.append("\0\0", 2); + return parse(buffer.data(), buffer.size(), foFile, path, dirOf(path), staticEnv); } -Expr * EvalState::parseExprFromString(std::string_view s, const Path & basePath, StaticEnv & staticEnv) +Expr * EvalState::parseExprFromString(std::string s, const Path & basePath, StaticEnv & staticEnv) { - return parse(s.data(), foString, "", basePath, staticEnv); + s.append("\0\0", 2); + return parse(s.data(), s.size(), foString, "", basePath, staticEnv); } -Expr * EvalState::parseExprFromString(std::string_view s, const Path & basePath) +Expr * EvalState::parseExprFromString(std::string s, const Path & basePath) { - return parseExprFromString(s, basePath, staticBaseEnv); + return parseExprFromString(std::move(s), basePath, staticBaseEnv); } Expr * EvalState::parseStdin() { //Activity act(*logger, lvlTalkative, format("parsing standard input")); - return parse(drainFD(0).data(), foStdin, "", absPath("."), staticBaseEnv); + auto buffer = drainFD(0); + // drainFD should have left some extra space for terminators + buffer.append("\0\0", 2); + return parse(buffer.data(), buffer.size(), foStdin, "", absPath("."), staticBaseEnv); } diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 66af373d7..852317aa3 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -350,7 +350,7 @@ void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v) auto output = runProgram(program, true, commandArgs); Expr * parsed; try { - parsed = state.parseExprFromString(output, pos.file); + parsed = state.parseExprFromString(std::move(output), pos.file); } catch (Error & e) { e.addTrace(pos, "While parsing the output from '%1%'", program); throw; @@ -3800,9 +3800,12 @@ void EvalState::createBaseEnv() /* Note: we have to initialize the 'derivation' constant *after* building baseEnv/staticBaseEnv because it uses 'builtins'. */ - eval(parse( + char code[] = #include "primops/derivation.nix.gen.hh" - , foFile, sDerivationNix, "/", staticBaseEnv), *vDerivation); + // the parser needs two NUL bytes as terminators; one of them + // is implied by being a C string. + "\0"; + eval(parse(code, sizeof(code), foFile, sDerivationNix, "/", staticBaseEnv), *vDerivation); } -- cgit v1.2.3 From 72f42093e711db1ab43c920688bb5e59df33935d Mon Sep 17 00:00:00 2001 From: pennae Date: Tue, 21 Dec 2021 10:28:05 +0100 Subject: optimize unescapeStr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mainly to avoid an allocation and a copy of a string that can be modified in place (ever since EvalState holds on to the buffer, not the generated parser itself). # before Benchmark 1: nix search --offline nixpkgs hello Time (mean ± σ): 571.7 ms ± 2.4 ms [User: 563.3 ms, System: 8.0 ms] Range (min … max): 566.7 ms … 579.7 ms 50 runs Benchmark 2: nix eval -f ../nixpkgs/pkgs/development/haskell-modules/hackage-packages.nix Time (mean ± σ): 376.6 ms ± 1.0 ms [User: 345.8 ms, System: 30.5 ms] Range (min … max): 374.5 ms … 379.1 ms 50 runs Benchmark 3: nix eval --raw --impure --expr 'with import {}; system' Time (mean ± σ): 2.922 s ± 0.006 s [User: 2.707 s, System: 0.215 s] Range (min … max): 2.906 s … 2.934 s 50 runs # after Benchmark 1: nix search --offline nixpkgs hello Time (mean ± σ): 570.4 ms ± 2.8 ms [User: 561.3 ms, System: 8.6 ms] Range (min … max): 564.6 ms … 578.1 ms 50 runs Benchmark 2: nix eval -f ../nixpkgs/pkgs/development/haskell-modules/hackage-packages.nix Time (mean ± σ): 375.4 ms ± 1.3 ms [User: 343.2 ms, System: 31.7 ms] Range (min … max): 373.4 ms … 378.2 ms 50 runs Benchmark 3: nix eval --raw --impure --expr 'with import {}; system' Time (mean ± σ): 2.925 s ± 0.006 s [User: 2.704 s, System: 0.219 s] Range (min … max): 2.910 s … 2.942 s 50 runs --- src/libexpr/lexer.l | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/lexer.l b/src/libexpr/lexer.l index 70e99d2d2..a0e7a1877 100644 --- a/src/libexpr/lexer.l +++ b/src/libexpr/lexer.l @@ -64,29 +64,32 @@ static void adjustLoc(YYLTYPE * loc, const char * s, size_t len) } -// FIXME: optimize -static Expr * unescapeStr(SymbolTable & symbols, const char * s, size_t length) +// we make use of the fact that the parser receives a private copy of the input +// string and can munge around in it. +static Expr * unescapeStr(SymbolTable & symbols, char * s, size_t length) { - string t; - t.reserve(length); + char * result = s; + char * t = s; char c; + // the input string is terminated with *two* NULs, so we can safely take + // *one* character after the one being checked against. while ((c = *s++)) { if (c == '\\') { - assert(*s); c = *s++; - if (c == 'n') t += '\n'; - else if (c == 'r') t += '\r'; - else if (c == 't') t += '\t'; - else t += c; + if (c == 'n') *t = '\n'; + else if (c == 'r') *t = '\r'; + else if (c == 't') *t = '\t'; + else *t = c; } else if (c == '\r') { /* Normalise CR and CR/LF into LF. */ - t += '\n'; + *t = '\n'; if (*s == '\n') s++; /* cr/lf */ } - else t += c; + else *t = c; + t++; } - return new ExprString(symbols.create(t)); + return new ExprString(symbols.create({result, size_t(t - result)})); } -- cgit v1.2.3 From c9fc975259e27220caeb4291f3dff453e65f1965 Mon Sep 17 00:00:00 2001 From: pennae Date: Sun, 2 Jan 2022 00:41:21 +0100 Subject: optimize removeAttrs builtin use a sorted array of symbols to be removed instead of a set. this saves a lot of memory allocations and slightly speeds up removal. --- src/libexpr/attr-set.hh | 7 +++++++ src/libexpr/primops.cc | 20 +++++++++++++------- 2 files changed, 20 insertions(+), 7 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/attr-set.hh b/src/libexpr/attr-set.hh index 82c348287..3e4899efc 100644 --- a/src/libexpr/attr-set.hh +++ b/src/libexpr/attr-set.hh @@ -121,6 +121,8 @@ class BindingsBuilder Bindings * bindings; public: + // needed by std::back_inserter + using value_type = Attr; EvalState & state; @@ -134,6 +136,11 @@ public: } void insert(const Attr & attr) + { + push_back(attr); + } + + void push_back(const Attr & attr) { bindings->push_back(attr); } diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 003e588a4..839fbb95c 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -12,6 +12,8 @@ #include "value-to-xml.hh" #include "primops.hh" +#include + #include #include #include @@ -2270,21 +2272,25 @@ static void prim_removeAttrs(EvalState & state, const Pos & pos, Value * * args, state.forceAttrs(*args[0], pos); state.forceList(*args[1], pos); - /* Get the attribute names to be removed. */ - std::set names; + /* Get the attribute names to be removed. + We keep them as Attrs instead of Symbols so std::set_difference + can be used to remove them from attrs[0]. */ + boost::container::small_vector names; + names.reserve(args[1]->listSize()); for (auto elem : args[1]->listItems()) { state.forceStringNoCtx(*elem, pos); - names.insert(state.symbols.create(elem->string.s)); + names.emplace_back(state.symbols.create(elem->string.s), nullptr); } + std::sort(names.begin(), names.end()); /* Copy all attributes not in that set. Note that we don't need to sort v.attrs because it's a subset of an already sorted vector. */ auto attrs = state.buildBindings(args[0]->attrs->size()); - for (auto & i : *args[0]->attrs) { - if (!names.count(i.name)) - attrs.insert(i); - } + std::set_difference( + args[0]->attrs->begin(), args[0]->attrs->end(), + names.begin(), names.end(), + std::back_inserter(attrs)); v.mkAttrs(attrs.alreadySorted()); } -- cgit v1.2.3 From ad60dfde2af6bcdb77e50562a2f2b28107e28588 Mon Sep 17 00:00:00 2001 From: pennae Date: Sun, 2 Jan 2022 00:46:43 +0100 Subject: also cache split regexes, not just match regexes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gives about 1% improvement on system eval, a bit less on nix search. # before nix search --no-eval-cache --offline ../nixpkgs hello Time (mean ± σ): 7.419 s ± 0.045 s [User: 6.362 s, System: 0.794 s] Range (min … max): 7.335 s … 7.517 s 20 runs nix eval --raw --impure --expr 'with import {}; system' Time (mean ± σ): 2.921 s ± 0.023 s [User: 2.626 s, System: 0.210 s] Range (min … max): 2.883 s … 2.957 s 20 runs # after nix search --no-eval-cache --offline ../nixpkgs hello Time (mean ± σ): 7.370 s ± 0.059 s [User: 6.333 s, System: 0.791 s] Range (min … max): 7.286 s … 7.541 s 20 runs nix eval --raw --impure --expr 'with import {}; system' Time (mean ± σ): 2.891 s ± 0.033 s [User: 2.606 s, System: 0.210 s] Range (min … max): 2.823 s … 2.958 s 20 runs --- src/libexpr/eval.hh | 1 + src/libexpr/primops.cc | 8 +++++--- 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 348a1b291..63d16bd2b 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -400,6 +400,7 @@ private: friend struct ExprSelect; friend void prim_getAttr(EvalState & state, const Pos & pos, Value * * args, Value & v); friend void prim_match(EvalState & state, const Pos & pos, Value * * args, Value & v); + friend void prim_split(EvalState & state, const Pos & pos, Value * * args, Value & v); friend struct Value; }; diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 839fbb95c..3004e9a0a 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -3528,18 +3528,20 @@ static RegisterPrimOp primop_match({ /* Split a string with a regular expression, and return a list of the non-matching parts interleaved by the lists of the matching groups. */ -static void prim_split(EvalState & state, const Pos & pos, Value * * args, Value & v) +void prim_split(EvalState & state, const Pos & pos, Value * * args, Value & v) { auto re = state.forceStringNoCtx(*args[0], pos); try { - std::regex regex(re, std::regex::extended); + auto regex = state.regexCache->cache.find(re); + if (regex == state.regexCache->cache.end()) + regex = state.regexCache->cache.emplace(re, std::regex(re, std::regex::extended)).first; PathSet context; const std::string str = state.forceString(*args[1], context, pos); - auto begin = std::sregex_iterator(str.begin(), str.end(), regex); + auto begin = std::sregex_iterator(str.begin(), str.end(), regex->second); auto end = std::sregex_iterator(); // Any matches results are surrounded by non-matching results. -- cgit v1.2.3 From 2dead2092470f7b0440d34035e19b9d8c80db91e Mon Sep 17 00:00:00 2001 From: tomberek Date: Fri, 14 Jan 2022 09:16:34 -0500 Subject: Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Théophane Hufschmitt <7226587+thufschmitt@users.noreply.github.com> --- src/libexpr/flake/flakeref.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/libexpr') diff --git a/src/libexpr/flake/flakeref.cc b/src/libexpr/flake/flakeref.cc index b3f01746e..06f8511b6 100644 --- a/src/libexpr/flake/flakeref.cc +++ b/src/libexpr/flake/flakeref.cc @@ -136,7 +136,7 @@ std::pair parseFlakeRefWithFragment( throw Error("unable to find a flake before encountering git boundary at '%s'", path); else { if (lstat(path).st_dev != device) - throw Error("unable to find a flake before encountering filesystem boundary at '%s'", path); + throw Error("path '%s' is not part of a flake (neither it nor its parent directories contain a 'flake.nix' file)", origPath); } path = dirOf(path); } -- cgit v1.2.3 From e3690ab39382498eaabbd07e696335e17c9f209c Mon Sep 17 00:00:00 2001 From: Alexander Bantyev Date: Fri, 14 Jan 2022 17:15:45 +0300 Subject: Add more tests for flake upward searching --- src/libexpr/flake/flakeref.cc | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/flake/flakeref.cc b/src/libexpr/flake/flakeref.cc index 06f8511b6..930ed9ccd 100644 --- a/src/libexpr/flake/flakeref.cc +++ b/src/libexpr/flake/flakeref.cc @@ -122,27 +122,28 @@ std::pair parseFlakeRefWithFragment( if (isFlake) { - if (!allowMissing && !pathExists(path + "/flake.nix")){ - notice("path '%s' does not contain a 'flake.nix', searching up",path); - - // Save device to detect filesystem boundary - dev_t device = lstat(path).st_dev; - bool found = false; - while (path != "/") { - if (pathExists(path + "/flake.nix")) { - found = true; - break; - } else if (pathExists(path + "/.git")) - throw Error("unable to find a flake before encountering git boundary at '%s'", path); - else { - if (lstat(path).st_dev != device) - throw Error("path '%s' is not part of a flake (neither it nor its parent directories contain a 'flake.nix' file)", origPath); + if (!allowMissing && !pathExists(path + "/flake.nix")){ + notice("path '%s' does not contain a 'flake.nix', searching up",path); + + // Save device to detect filesystem boundary + dev_t device = lstat(path).st_dev; + bool found = false; + while (path != "/") { + if (pathExists(path + "/flake.nix")) { + found = true; + break; + } else if (pathExists(path + "/.git")) + throw Error("path '%s' is not part of a flake (neither it nor its parent directories contain a 'flake.nix' file)", path); + else { + if (lstat(path).st_dev != device) + throw Error("unable to find a flake before encountering filesystem boundary at '%s'", path); + } + path = dirOf(path); } - path = dirOf(path); + if (!found) + throw BadURL("could not find a flake.nix file"); } - if (!found) - throw BadURL("could not find a flake.nix file"); - } + if (!S_ISDIR(lstat(path).st_mode)) throw BadURL("path '%s' is not a flake (because it's not a directory)", path); -- cgit v1.2.3 From 3e5a9ad7ff15e5263d8b31288095ebd2e489b8e1 Mon Sep 17 00:00:00 2001 From: "lincoln auster [they/them]" Date: Thu, 13 Jan 2022 13:01:04 -0700 Subject: allow modifying lockfile commit msg with nix config option This allows setting the commit-lockfile-summary option to a non-empty string to override the commit summary while leaving the body unchanged. --- src/libexpr/flake/flake.cc | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc index 190a128d7..0fbe9b960 100644 --- a/src/libexpr/flake/flake.cc +++ b/src/libexpr/flake/flake.cc @@ -633,12 +633,24 @@ LockedFlake lockFlake( newLockFile.write(path); + std::optional commitMessage = std::nullopt; + if (lockFlags.commitLockFile) { + std::string cm; + + cm = settings.commitLockFileSummary.get(); + + if (cm == "") { + cm = fmt("%s: %s", relPath, lockFileExists ? "Update" : "Add"); + } + + cm += "\n\nFlake lock file updates:\n\n"; + cm += filterANSIEscapes(diff, true); + commitMessage = cm; + } + topRef.input.markChangedFile( (topRef.subdir == "" ? "" : topRef.subdir + "/") + "flake.lock", - lockFlags.commitLockFile - ? std::optional(fmt("%s: %s\n\nFlake lock file changes:\n\n%s", - relPath, lockFileExists ? "Update" : "Add", filterANSIEscapes(diff, true))) - : std::nullopt); + commitMessage); /* Rewriting the lockfile changed the top-level repo, so we should re-read it. FIXME: we could -- cgit v1.2.3 From 0a7746603eda58c4b368e977e87d0aa4db397f5b Mon Sep 17 00:00:00 2001 From: pennae Date: Wed, 19 Jan 2022 13:39:42 +0100 Subject: remove ExprIndStr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit it can be replaced with StringToken if we add another bit if information to StringToken, namely whether this string should take part in indentation scanning or not. since all escaping terminates indentation scanning we need to set this bit only for the non-escaped IND_STRING rule. this improves performance by about 1%. before nix search --no-eval-cache --offline ../nixpkgs hello Time (mean ± σ): 8.880 s ± 0.048 s [User: 6.809 s, System: 1.643 s] Range (min … max): 8.781 s … 8.993 s 20 runs nix eval -f ../nixpkgs/pkgs/development/haskell-modules/hackage-packages.nix Time (mean ± σ): 375.0 ms ± 2.2 ms [User: 339.8 ms, System: 35.2 ms] Range (min … max): 371.5 ms … 379.3 ms 20 runs nix eval --raw --impure --expr 'with import {}; system' Time (mean ± σ): 2.831 s ± 0.040 s [User: 2.536 s, System: 0.225 s] Range (min … max): 2.769 s … 2.912 s 20 runs after nix search --no-eval-cache --offline ../nixpkgs hello Time (mean ± σ): 8.832 s ± 0.048 s [User: 6.757 s, System: 1.657 s] Range (min … max): 8.743 s … 8.921 s 20 runs nix eval -f ../nixpkgs/pkgs/development/haskell-modules/hackage-packages.nix Time (mean ± σ): 367.4 ms ± 3.2 ms [User: 332.7 ms, System: 34.7 ms] Range (min … max): 364.6 ms … 374.6 ms 20 runs nix eval --raw --impure --expr 'with import {}; system' Time (mean ± σ): 2.810 s ± 0.030 s [User: 2.517 s, System: 0.225 s] Range (min … max): 2.742 s … 2.854 s 20 runs --- src/libexpr/lexer.l | 18 +++++------ src/libexpr/nixexpr.hh | 7 ---- src/libexpr/parser.y | 86 ++++++++++++++++++++++++++++---------------------- 3 files changed, 57 insertions(+), 54 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/lexer.l b/src/libexpr/lexer.l index a0e7a1877..e276b0467 100644 --- a/src/libexpr/lexer.l +++ b/src/libexpr/lexer.l @@ -66,7 +66,7 @@ static void adjustLoc(YYLTYPE * loc, const char * s, size_t len) // we make use of the fact that the parser receives a private copy of the input // string and can munge around in it. -static Expr * unescapeStr(SymbolTable & symbols, char * s, size_t length) +static StringToken unescapeStr(SymbolTable & symbols, char * s, size_t length) { char * result = s; char * t = s; @@ -89,7 +89,7 @@ static Expr * unescapeStr(SymbolTable & symbols, char * s, size_t length) else *t = c; t++; } - return new ExprString(symbols.create({result, size_t(t - result)})); + return {result, size_t(t - result)}; } @@ -176,7 +176,7 @@ or { return OR_KW; } /* It is impossible to match strings ending with '$' with one regex because trailing contexts are only valid at the end of a rule. (A sane but undocumented limitation.) */ - yylval->e = unescapeStr(data->symbols, yytext, yyleng); + yylval->str = unescapeStr(data->symbols, yytext, yyleng); return STR; } \$\{ { PUSH_STATE(DEFAULT); return DOLLAR_CURLY; } @@ -191,26 +191,26 @@ or { return OR_KW; } \'\'(\ *\n)? { PUSH_STATE(IND_STRING); return IND_STRING_OPEN; } ([^\$\']|\$[^\{\']|\'[^\'\$])+ { - yylval->e = new ExprIndStr(yytext); + yylval->str = {yytext, (size_t) yyleng, true}; return IND_STR; } \'\'\$ | \$ { - yylval->e = new ExprIndStr("$"); + yylval->str = {"$", 1}; return IND_STR; } \'\'\' { - yylval->e = new ExprIndStr("''"); + yylval->str = {"''", 2}; return IND_STR; } \'\'\\{ANY} { - yylval->e = unescapeStr(data->symbols, yytext + 2, yyleng - 2); + yylval->str = unescapeStr(data->symbols, yytext + 2, yyleng - 2); return IND_STR; } \$\{ { PUSH_STATE(DEFAULT); return DOLLAR_CURLY; } \'\' { POP_STATE(); return IND_STRING_CLOSE; } \' { - yylval->e = new ExprIndStr("'"); + yylval->str = {"'", 1}; return IND_STR; } @@ -264,7 +264,7 @@ or { return OR_KW; } PUSH_STATE(INPATH_SLASH); else PUSH_STATE(INPATH); - yylval->e = new ExprString(data->symbols.create(string(yytext))); + yylval->str = {yytext, (size_t) yyleng}; return STR; } {ANY} | diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index 0a60057e5..ae11560ea 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -117,13 +117,6 @@ struct ExprString : Expr Value * maybeThunk(EvalState & state, Env & env); }; -/* Temporary class used during parsing of indented strings. */ -struct ExprIndStr : Expr -{ - string s; - ExprIndStr(const string & s) : s(s) { }; -}; - struct ExprPath : Expr { string s; diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index a3e713937..38b218156 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -16,6 +16,8 @@ #ifndef BISON_HEADER #define BISON_HEADER +#include + #include "util.hh" #include "nixexpr.hh" @@ -41,6 +43,15 @@ namespace nix { } +// using C a struct allows us to avoid having to define the special +// members that using string_view here would implicitly delete. +struct StringToken { + const char * p; + size_t l; + bool hasIndentation; + operator std::string_view() const { return {p, l}; } +}; + #define YY_DECL int yylex \ (YYSTYPE * yylval_param, YYLTYPE * yylloc_param, yyscan_t yyscanner, nix::ParseData * data) @@ -152,7 +163,8 @@ static void addFormal(const Pos & pos, Formals * formals, const Formal & formal) } -static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector > & es) +static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, + vector > > & es) { if (es.empty()) return new ExprString(symbols.create("")); @@ -163,20 +175,20 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector(i); - if (!e) { - /* Anti-quotations end the current start-of-line whitespace. */ + auto * str = std::get_if(&i); + if (!str || !str->hasIndentation) { + /* Anti-quotations and escaped characters end the current start-of-line whitespace. */ if (atStartOfLine) { atStartOfLine = false; if (curIndent < minIndent) minIndent = curIndent; } continue; } - for (size_t j = 0; j < e->s.size(); ++j) { + for (size_t j = 0; j < str->l; ++j) { if (atStartOfLine) { - if (e->s[j] == ' ') + if (str->p[j] == ' ') curIndent++; - else if (e->s[j] == '\n') { + else if (str->p[j] == '\n') { /* Empty line, doesn't influence minimum indentation. */ curIndent = 0; @@ -184,7 +196,7 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vectors[j] == '\n') { + } else if (str->p[j] == '\n') { atStartOfLine = true; curIndent = 0; } @@ -196,33 +208,31 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector >::iterator i = es.begin(); i != es.end(); ++i, --n) { - ExprIndStr * e = dynamic_cast(i->second); - if (!e) { - atStartOfLine = false; - curDropped = 0; - es2->push_back(*i); - continue; - } - + auto i = es.begin(); + const auto trimExpr = [&] (Expr * e) { + atStartOfLine = false; + curDropped = 0; + es2->emplace_back(i->first, e); + }; + const auto trimString = [&] (const StringToken & t) { string s2; - for (size_t j = 0; j < e->s.size(); ++j) { + for (size_t j = 0; j < t.l; ++j) { if (atStartOfLine) { - if (e->s[j] == ' ') { + if (t.p[j] == ' ') { if (curDropped++ >= minIndent) - s2 += e->s[j]; + s2 += t.p[j]; } - else if (e->s[j] == '\n') { + else if (t.p[j] == '\n') { curDropped = 0; - s2 += e->s[j]; + s2 += t.p[j]; } else { atStartOfLine = false; curDropped = 0; - s2 += e->s[j]; + s2 += t.p[j]; } } else { - s2 += e->s[j]; - if (e->s[j] == '\n') atStartOfLine = true; + s2 += t.p[j]; + if (t.p[j] == '\n') atStartOfLine = true; } } @@ -235,6 +245,9 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vectoremplace_back(i->first, new ExprString(symbols.create(s2))); + }; + for (; i != es.end(); ++i, --n) { + std::visit(overloaded { trimExpr, trimString }, i->second); } /* If this is a single string, then don't do a concatenation. */ @@ -273,18 +286,13 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err nix::Formal * formal; nix::NixInt n; nix::NixFloat nf; - // using C a struct allows us to avoid having to define the special - // members that using string_view here would implicitly delete. - struct StringToken { - const char * p; - size_t l; - operator std::string_view() const { return {p, l}; } - }; StringToken id; // !!! -> Symbol StringToken path; StringToken uri; + StringToken str; std::vector * attrNames; std::vector > * string_parts; + std::vector > > * ind_string_parts; } %type start expr expr_function expr_if expr_op @@ -294,11 +302,12 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err %type formals %type formal %type attrs attrpath -%type string_parts_interpolated ind_string_parts +%type string_parts_interpolated +%type ind_string_parts %type path_start string_parts string_attr %type attr %token ID ATTRPATH -%token STR IND_STR +%token STR IND_STR %token INT %token FLOAT %token PATH HPATH SPATH PATH_END @@ -449,18 +458,19 @@ expr_simple ; string_parts - : STR + : STR { $$ = new ExprString(data->symbols.create($1)); } | string_parts_interpolated { $$ = new ExprConcatStrings(CUR_POS, true, $1); } | { $$ = new ExprString(data->symbols.create("")); } ; string_parts_interpolated - : string_parts_interpolated STR { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $2); } + : string_parts_interpolated STR + { $$ = $1; $1->emplace_back(makeCurPos(@2, data), new ExprString(data->symbols.create($2))); } | string_parts_interpolated DOLLAR_CURLY expr '}' { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $3); } | DOLLAR_CURLY expr '}' { $$ = new vector >; $$->emplace_back(makeCurPos(@1, data), $2); } | STR DOLLAR_CURLY expr '}' { $$ = new vector >; - $$->emplace_back(makeCurPos(@1, data), $1); + $$->emplace_back(makeCurPos(@1, data), new ExprString(data->symbols.create($1))); $$->emplace_back(makeCurPos(@2, data), $3); } ; @@ -482,7 +492,7 @@ path_start ind_string_parts : ind_string_parts IND_STR { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $2); } | ind_string_parts DOLLAR_CURLY expr '}' { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $3); } - | { $$ = new vector >; } + | { $$ = new vector > >; } ; binds -- cgit v1.2.3 From 9ac836d1d6d7e3480ad3bc4dbe23286d9052052b Mon Sep 17 00:00:00 2001 From: pennae Date: Wed, 19 Jan 2022 14:31:30 +0100 Subject: don't use Symbols for strings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit string expressions by and large do not need the benefits a Symbol gives us, instead they pollute the symbol table and cause unnecessary overhead for almost all strings. the one place we can think of that benefits from them (attrpaths with expressions) extracts the benefit in the parser, which we'll have to touch anyway when changing ExprString to hold strings. this gives a sizeable improvement on of 3-5% on all benchmarks we've run. before nix search --no-eval-cache --offline ../nixpkgs hello Time (mean ± σ): 8.844 s ± 0.045 s [User: 6.750 s, System: 1.663 s] Range (min … max): 8.758 s … 8.922 s 20 runs nix eval -f ../nixpkgs/pkgs/development/haskell-modules/hackage-packages.nix Time (mean ± σ): 367.4 ms ± 3.3 ms [User: 332.3 ms, System: 35.2 ms] Range (min … max): 364.0 ms … 375.2 ms 20 runs nix eval --raw --impure --expr 'with import {}; system' Time (mean ± σ): 2.810 s ± 0.030 s [User: 2.517 s, System: 0.225 s] Range (min … max): 2.742 s … 2.854 s 20 runs after nix search --no-eval-cache --offline ../nixpkgs hello Time (mean ± σ): 8.533 s ± 0.068 s [User: 6.485 s, System: 1.642 s] Range (min … max): 8.404 s … 8.657 s 20 runs nix eval -f ../nixpkgs/pkgs/development/haskell-modules/hackage-packages.nix Time (mean ± σ): 347.6 ms ± 3.1 ms [User: 313.1 ms, System: 34.5 ms] Range (min … max): 343.3 ms … 354.6 ms 20 runs nix eval --raw --impure --expr 'with import {}; system' Time (mean ± σ): 2.709 s ± 0.032 s [User: 2.414 s, System: 0.232 s] Range (min … max): 2.655 s … 2.788 s 20 runs --- src/libexpr/nixexpr.hh | 4 ++-- src/libexpr/parser.y | 22 +++++++++++----------- 2 files changed, 13 insertions(+), 13 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index ae11560ea..a2070ff69 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -110,9 +110,9 @@ struct ExprFloat : Expr struct ExprString : Expr { - Symbol s; + string s; Value v; - ExprString(const Symbol & s) : s(s) { v.mkString(s); }; + ExprString(std::string s) : s(std::move(s)) { v.mkString(this->s.data()); }; COMMON_METHODS Value * maybeThunk(EvalState & state, Env & env); }; diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 38b218156..88bdebcd6 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -166,7 +166,7 @@ static void addFormal(const Pos & pos, Formals * formals, const Formal & formal) static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector > > & es) { - if (es.empty()) return new ExprString(symbols.create("")); + if (es.empty()) return new ExprString(""); /* Figure out the minimum indentation. Note that by design whitespace-only final lines are not taken into account. (So @@ -244,7 +244,7 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, s2 = string(s2, 0, p + 1); } - es2->emplace_back(i->first, new ExprString(symbols.create(s2))); + es2->emplace_back(i->first, new ExprString(s2)); }; for (; i != es.end(); ++i, --n) { std::visit(overloaded { trimExpr, trimString }, i->second); @@ -434,7 +434,7 @@ expr_simple $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__findFile")), {new ExprVar(data->symbols.create("__nixPath")), - new ExprString(data->symbols.create(path))}); + new ExprString(path)}); } | URI { static bool noURLLiterals = settings.isExperimentalFeatureEnabled(Xp::NoUrlLiterals); @@ -443,7 +443,7 @@ expr_simple .msg = hintfmt("URL literals are disabled"), .errPos = CUR_POS }); - $$ = new ExprString(data->symbols.create($1)); + $$ = new ExprString(string($1)); } | '(' expr ')' { $$ = $2; } /* Let expressions `let {..., body = ...}' are just desugared @@ -458,19 +458,19 @@ expr_simple ; string_parts - : STR { $$ = new ExprString(data->symbols.create($1)); } + : STR { $$ = new ExprString(string($1)); } | string_parts_interpolated { $$ = new ExprConcatStrings(CUR_POS, true, $1); } - | { $$ = new ExprString(data->symbols.create("")); } + | { $$ = new ExprString(""); } ; string_parts_interpolated : string_parts_interpolated STR - { $$ = $1; $1->emplace_back(makeCurPos(@2, data), new ExprString(data->symbols.create($2))); } + { $$ = $1; $1->emplace_back(makeCurPos(@2, data), new ExprString(string($2))); } | string_parts_interpolated DOLLAR_CURLY expr '}' { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $3); } | DOLLAR_CURLY expr '}' { $$ = new vector >; $$->emplace_back(makeCurPos(@1, data), $2); } | STR DOLLAR_CURLY expr '}' { $$ = new vector >; - $$->emplace_back(makeCurPos(@1, data), new ExprString(data->symbols.create($1))); + $$->emplace_back(makeCurPos(@1, data), new ExprString(string($1))); $$->emplace_back(makeCurPos(@2, data), $3); } ; @@ -524,7 +524,7 @@ attrs { $$ = $1; ExprString * str = dynamic_cast($2); if (str) { - $$->push_back(AttrName(str->s)); + $$->push_back(AttrName(data->symbols.create(str->s))); delete str; } else throw ParseError({ @@ -541,7 +541,7 @@ attrpath { $$ = $1; ExprString * str = dynamic_cast($3); if (str) { - $$->push_back(AttrName(str->s)); + $$->push_back(AttrName(data->symbols.create(str->s))); delete str; } else $$->push_back(AttrName($3)); @@ -551,7 +551,7 @@ attrpath { $$ = new vector; ExprString *str = dynamic_cast($1); if (str) { - $$->push_back(AttrName(str->s)); + $$->push_back(AttrName(data->symbols.create(str->s))); delete str; } else $$->push_back(AttrName($1)); -- cgit v1.2.3 From 7d4cc5515ce5e4972fd5b474ae18cf95f6ea257e Mon Sep 17 00:00:00 2001 From: pennae Date: Wed, 19 Jan 2022 16:49:02 +0100 Subject: defer formals duplicate check for incresed efficiency all round MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit if we defer the duplicate argument check for lambda formals we can use more efficient data structures for the formals set, and we can get rid of the duplication of formals names to boot. instead of a list of formals we've seen and a set of names we'll keep a vector instead and run a sort+dupcheck step before moving the parsed formals into a newly created lambda. this improves performance on search and rebuild by ~1%, pure parsing gains more (about 4%). this does reorder lambda arguments in the xml output, but the output is still stable. this shouldn't be a problem since argument order is not semantically important anyway. before nix search --no-eval-cache --offline ../nixpkgs hello Time (mean ± σ): 8.550 s ± 0.060 s [User: 6.470 s, System: 1.664 s] Range (min … max): 8.435 s … 8.666 s 20 runs nix eval -f ../nixpkgs/pkgs/development/haskell-modules/hackage-packages.nix Time (mean ± σ): 346.7 ms ± 2.1 ms [User: 312.4 ms, System: 34.2 ms] Range (min … max): 343.8 ms … 353.4 ms 20 runs nix eval --raw --impure --expr 'with import {}; system' Time (mean ± σ): 2.720 s ± 0.031 s [User: 2.415 s, System: 0.231 s] Range (min … max): 2.662 s … 2.780 s 20 runs after nix search --no-eval-cache --offline ../nixpkgs hello Time (mean ± σ): 8.462 s ± 0.063 s [User: 6.398 s, System: 1.661 s] Range (min … max): 8.339 s … 8.542 s 20 runs nix eval -f ../nixpkgs/pkgs/development/haskell-modules/hackage-packages.nix Time (mean ± σ): 329.1 ms ± 1.4 ms [User: 296.8 ms, System: 32.3 ms] Range (min … max): 326.1 ms … 330.8 ms 20 runs nix eval --raw --impure --expr 'with import {}; system' Time (mean ± σ): 2.687 s ± 0.035 s [User: 2.392 s, System: 0.228 s] Range (min … max): 2.626 s … 2.754 s 20 runs --- src/libexpr/eval.cc | 2 +- src/libexpr/nixexpr.hh | 24 ++++++++++++------ src/libexpr/parser.y | 61 +++++++++++++++++++++++++++++++++++---------- src/libexpr/value-to-xml.cc | 2 +- 4 files changed, 67 insertions(+), 22 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 94ffab175..73ed3a8a1 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -1368,7 +1368,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & /* Nope, so show the first unexpected argument to the user. */ for (auto & i : *args[0]->attrs) - if (!lambda.formals->argNames.count(i.name)) + if (!lambda.formals->has(i.name)) throwTypeError(pos, "%1% called with unexpected argument '%2%'", lambda, i.name); abort(); // can't happen } diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index a2070ff69..4e923ac89 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -216,10 +216,25 @@ struct Formal struct Formals { - typedef std::list Formals_; + typedef std::vector Formals_; Formals_ formals; - std::set argNames; // used during parsing bool ellipsis; + + bool has(Symbol arg) const { + auto it = std::lower_bound(formals.begin(), formals.end(), arg, + [] (const Formal & f, const Symbol & sym) { return f.name < sym; }); + return it != formals.end() && it->name == arg; + } + + std::vector lexicographicOrder() const + { + std::vector result(formals.begin(), formals.end()); + std::sort(result.begin(), result.end(), + [] (const Formal & a, const Formal & b) { + return std::string_view(a.name) < std::string_view(b.name); + }); + return result; + } }; struct ExprLambda : Expr @@ -232,11 +247,6 @@ struct ExprLambda : Expr ExprLambda(const Pos & pos, const Symbol & arg, Formals * formals, Expr * body) : pos(pos), arg(arg), formals(formals), body(body) { - if (!arg.empty() && formals && formals->argNames.find(arg) != formals->argNames.end()) - throw ParseError({ - .msg = hintfmt("duplicate formal function argument '%1%'", arg), - .errPos = pos - }); }; void setName(Symbol & name); string showNamePos() const; diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 88bdebcd6..1b0177e4a 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -41,6 +41,11 @@ namespace nix { { }; }; + struct ParserFormals { + std::vector formals; + bool ellipsis = false; + }; + } // using C a struct allows us to avoid having to define the special @@ -151,15 +156,39 @@ static void addAttr(ExprAttrs * attrs, AttrPath & attrPath, } -static void addFormal(const Pos & pos, Formals * formals, const Formal & formal) +static Formals * toFormals(ParseData & data, ParserFormals * formals, + Pos pos = noPos, Symbol arg = {}) { - if (!formals->argNames.insert(formal.name).second) + std::sort(formals->formals.begin(), formals->formals.end(), + [] (const auto & a, const auto & b) { + return std::tie(a.name, a.pos) < std::tie(b.name, b.pos); + }); + + std::optional> duplicate; + for (size_t i = 0; i + 1 < formals->formals.size(); i++) { + if (formals->formals[i].name != formals->formals[i + 1].name) + continue; + std::pair thisDup{formals->formals[i].name, formals->formals[i + 1].pos}; + duplicate = std::min(thisDup, duplicate.value_or(thisDup)); + } + if (duplicate) throw ParseError({ - .msg = hintfmt("duplicate formal function argument '%1%'", - formal.name), + .msg = hintfmt("duplicate formal function argument '%1%'", duplicate->first), + .errPos = duplicate->second + }); + + Formals result; + result.ellipsis = formals->ellipsis; + result.formals = std::move(formals->formals); + + if (arg.set() && result.has(arg)) + throw ParseError({ + .msg = hintfmt("duplicate formal function argument '%1%'", arg), .errPos = pos }); - formals->formals.push_front(formal); + + delete formals; + return new Formals(std::move(result)); } @@ -282,7 +311,7 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err nix::Expr * e; nix::ExprList * list; nix::ExprAttrs * attrs; - nix::Formals * formals; + nix::ParserFormals * formals; nix::Formal * formal; nix::NixInt n; nix::NixFloat nf; @@ -340,11 +369,17 @@ expr_function : ID ':' expr_function { $$ = new ExprLambda(CUR_POS, data->symbols.create($1), 0, $3); } | '{' formals '}' ':' expr_function - { $$ = new ExprLambda(CUR_POS, data->symbols.create(""), $2, $5); } + { $$ = new ExprLambda(CUR_POS, data->symbols.create(""), toFormals(*data, $2), $5); } | '{' formals '}' '@' ID ':' expr_function - { $$ = new ExprLambda(CUR_POS, data->symbols.create($5), $2, $7); } + { + Symbol arg = data->symbols.create($5); + $$ = new ExprLambda(CUR_POS, arg, toFormals(*data, $2, CUR_POS, arg), $7); + } | ID '@' '{' formals '}' ':' expr_function - { $$ = new ExprLambda(CUR_POS, data->symbols.create($1), $4, $7); } + { + Symbol arg = data->symbols.create($1); + $$ = new ExprLambda(CUR_POS, arg, toFormals(*data, $4, CUR_POS, arg), $7); + } | ASSERT expr ';' expr_function { $$ = new ExprAssert(CUR_POS, $2, $4); } | WITH expr ';' expr_function @@ -575,13 +610,13 @@ expr_list formals : formal ',' formals - { $$ = $3; addFormal(CUR_POS, $$, *$1); } + { $$ = $3; $$->formals.push_back(*$1); } | formal - { $$ = new Formals; addFormal(CUR_POS, $$, *$1); $$->ellipsis = false; } + { $$ = new ParserFormals; $$->formals.push_back(*$1); $$->ellipsis = false; } | - { $$ = new Formals; $$->ellipsis = false; } + { $$ = new ParserFormals; $$->ellipsis = false; } | ELLIPSIS - { $$ = new Formals; $$->ellipsis = true; } + { $$ = new ParserFormals; $$->ellipsis = true; } ; formal diff --git a/src/libexpr/value-to-xml.cc b/src/libexpr/value-to-xml.cc index a875f82d7..a9fb60b0e 100644 --- a/src/libexpr/value-to-xml.cc +++ b/src/libexpr/value-to-xml.cc @@ -142,7 +142,7 @@ static void printValueAsXML(EvalState & state, bool strict, bool location, if (!v.lambda.fun->arg.empty()) attrs["name"] = v.lambda.fun->arg; if (v.lambda.fun->formals->ellipsis) attrs["ellipsis"] = "1"; XMLOpenElement _(doc, "attrspat", attrs); - for (auto & i : v.lambda.fun->formals->formals) + for (auto & i : v.lambda.fun->formals->lexicographicOrder()) doc.writeEmptyElement("attr", singletonAttrs("name", i.name)); } else doc.writeEmptyElement("varpat", singletonAttrs("name", v.lambda.fun->arg)); -- cgit v1.2.3 From 128098040bc2bed1555b701dae17d5969cf2ce2d Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 21 Jan 2022 13:51:05 +0100 Subject: Fix exception handling around realisePath() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This no longer worked correctly because 'path' is uninitialised when an exception occurs, leading to errors like … while importing '' at /nix/store/rrzz5b1pshvzh1437ac9nkl06br81lkv-source/flake.nix:352:13: So move the adding of the error context into realisePath(). --- src/libexpr/primops.cc | 115 ++++++++++++++----------------------------------- 1 file changed, 33 insertions(+), 82 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 5c476ddd4..074181e13 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -102,17 +102,30 @@ static Path realisePath(EvalState & state, const Pos & pos, Value & v, const Rea { PathSet context; - Path path = flags.requireAbsolutePath - ? state.coerceToPath(pos, v, context) - : state.coerceToString(pos, v, context, false, false); + auto path = [&]() + { + try { + return flags.requireAbsolutePath + ? state.coerceToPath(pos, v, context) + : state.coerceToString(pos, v, context, false, false); + } catch (Error & e) { + e.addTrace(pos, "while realising the context of a path"); + throw; + } + }(); - StringMap rewrites = state.realiseContext(context); + try { + StringMap rewrites = state.realiseContext(context); - auto realPath = state.toRealPath(rewriteStrings(path, rewrites), context); + auto realPath = state.toRealPath(rewriteStrings(path, rewrites), context); - return flags.checkForPureEval - ? state.checkSourcePath(realPath) - : realPath; + return flags.checkForPureEval + ? state.checkSourcePath(realPath) + : realPath; + } catch (Error & e) { + e.addTrace(pos, "while realising the context of path '%s'", path); + throw; + } } /* Add and attribute to the given attribute map from the output name to @@ -150,18 +163,7 @@ static void mkOutputString( argument. */ static void import(EvalState & state, const Pos & pos, Value & vPath, Value * vScope, Value & v) { - Path path; - try { - path = realisePath(state, pos, vPath); - } catch (InvalidPathError & e) { - throw EvalError({ - .msg = hintfmt("cannot import '%1%', since path '%2%' is not valid", path, e.path), - .errPos = pos - }); - } catch (Error & e) { - e.addTrace(pos, "while importing '%s'", path); - throw; - } + auto path = realisePath(state, pos, vPath); // FIXME auto isValidDerivationInStore = [&]() -> std::optional { @@ -314,18 +316,7 @@ extern "C" typedef void (*ValueInitializer)(EvalState & state, Value & v); /* Load a ValueInitializer from a DSO and return whatever it initializes */ void prim_importNative(EvalState & state, const Pos & pos, Value * * args, Value & v) { - Path path; - try { - path = realisePath(state, pos, *args[0]); - } catch (InvalidPathError & e) { - throw EvalError({ - .msg = hintfmt("cannot import '%1%', since path '%2%' is not valid", path, e.path), - .errPos = pos - }); - } catch (Error & e) { - e.addTrace(pos, "while importing '%s'", path); - throw; - } + auto path = realisePath(state, pos, *args[0]); string sym = state.forceStringNoCtx(*args[1], pos); @@ -1379,22 +1370,12 @@ static RegisterPrimOp primop_storePath({ static void prim_pathExists(EvalState & state, const Pos & pos, Value * * args, Value & v) { - Path path; - try { - // We don’t check the path right now, because we don’t want to throw if - // the path isn’t allowed, but just return false - // (and we can’t just catch the exception here because we still want to - // throw if something in the evaluation of `*args[0]` tries to access an - // unauthorized path) - path = realisePath(state, pos, *args[0], { .checkForPureEval = false }); - } catch (InvalidPathError & e) { - throw EvalError({ - .msg = hintfmt( - "cannot check the existence of '%1%', since path '%2%' is not valid", - path, e.path), - .errPos = pos - }); - } + /* We don’t check the path right now, because we don’t want to + throw if the path isn’t allowed, but just return false (and we + can’t just catch the exception here because we still want to + throw if something in the evaluation of `*args[0]` tries to + access an unauthorized path). */ + auto path = realisePath(state, pos, *args[0], { .checkForPureEval = false }); try { v.mkBool(pathExists(state.checkSourcePath(path))); @@ -1460,15 +1441,7 @@ static RegisterPrimOp primop_dirOf({ /* Return the contents of a file as a string. */ static void prim_readFile(EvalState & state, const Pos & pos, Value * * args, Value & v) { - Path path; - try { - path = realisePath(state, pos, *args[0]); - } catch (InvalidPathError & e) { - throw EvalError({ - .msg = hintfmt("cannot read '%1%', since path '%2%' is not valid", path, e.path), - .errPos = pos - }); - } + auto path = realisePath(state, pos, *args[0]); string s = readFile(path); if (s.find((char) 0) != string::npos) throw Error("the contents of the file '%1%' cannot be represented as a Nix string", path); @@ -1512,16 +1485,7 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va pos ); - Path path; - - try { - path = realisePath(state, pos, *i->value, { .requireAbsolutePath = false }); - } catch (InvalidPathError & e) { - throw EvalError({ - .msg = hintfmt("cannot find '%1%', since path '%2%' is not valid", path, e.path), - .errPos = pos - }); - } + auto path = realisePath(state, pos, *i->value, { .requireAbsolutePath = false }); searchPath.emplace_back(prefix, path); } @@ -1548,12 +1512,7 @@ static void prim_hashFile(EvalState & state, const Pos & pos, Value * * args, Va .errPos = pos }); - Path path; - try { - path = realisePath(state, pos, *args[1]); - } catch (InvalidPathError & e) { - throw EvalError("cannot read '%s' since path '%s' is not valid, at %s", path, e.path, pos); - } + auto path = realisePath(state, pos, *args[1]); v.mkString(hashFile(*ht, path).to_string(Base16, false)); } @@ -1572,15 +1531,7 @@ static RegisterPrimOp primop_hashFile({ /* Read a directory (without . or ..) */ static void prim_readDir(EvalState & state, const Pos & pos, Value * * args, Value & v) { - Path path; - try { - path = realisePath(state, pos, *args[0]); - } catch (InvalidPathError & e) { - throw EvalError({ - .msg = hintfmt("cannot read '%1%', since path '%2%' is not valid", path, e.path), - .errPos = pos - }); - } + auto path = realisePath(state, pos, *args[0]); DirEntries entries = readDirectory(path); -- cgit v1.2.3 From 49b0bb020663b7283549754a826b1346c93a8bbb Mon Sep 17 00:00:00 2001 From: Kevin Amado Date: Sat, 27 Nov 2021 12:40:24 -0500 Subject: forceValue: make pos mandatory - Make passing the position to `forceValue` mandatory, this way we remember people that the position is important for better error messages - Add pos to all `forceValue` calls --- src/libexpr/attr-path.cc | 2 +- src/libexpr/eval-cache.cc | 2 +- src/libexpr/eval-inline.hh | 4 ++-- src/libexpr/eval.cc | 17 +++++++++-------- src/libexpr/eval.hh | 2 +- src/libexpr/get-drvs.cc | 4 ++-- 6 files changed, 16 insertions(+), 15 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/attr-path.cc b/src/libexpr/attr-path.cc index c50c6d92b..8ec24269f 100644 --- a/src/libexpr/attr-path.cc +++ b/src/libexpr/attr-path.cc @@ -58,7 +58,7 @@ std::pair findAlongAttrPath(EvalState & state, const string & attr Value * vNew = state.allocValue(); state.autoCallFunction(autoArgs, *v, *vNew); v = vNew; - state.forceValue(*v); + state.forceValue(*v, v->determinePos(vIn.determinePos(noPos))); /* It should evaluate to either a set or an expression, according to what is specified in the attrPath. */ diff --git a/src/libexpr/eval-cache.cc b/src/libexpr/eval-cache.cc index d7e21783d..a92acafa8 100644 --- a/src/libexpr/eval-cache.cc +++ b/src/libexpr/eval-cache.cc @@ -381,7 +381,7 @@ Value & AttrCursor::forceValue() auto & v = getValue(); try { - root->state.forceValue(v); + root->state.forceValue(v, v.determinePos(noPos)); } catch (EvalError &) { debug("setting '%s' to failed", getAttrPathStr()); if (root->db) diff --git a/src/libexpr/eval-inline.hh b/src/libexpr/eval-inline.hh index 655408cd3..cd58315ba 100644 --- a/src/libexpr/eval-inline.hh +++ b/src/libexpr/eval-inline.hh @@ -53,7 +53,7 @@ void EvalState::forceValue(Value & v, const Pos & pos) inline void EvalState::forceAttrs(Value & v) { - forceValue(v); + forceValue(v, v.determinePos(noPos)); if (v.type() != nAttrs) throwTypeError("value is %1% while a set was expected", v); } @@ -69,7 +69,7 @@ inline void EvalState::forceAttrs(Value & v, const Pos & pos) inline void EvalState::forceList(Value & v) { - forceValue(v); + forceValue(v, v.determinePos(noPos)); if (!v.isList()) throwTypeError("value is %1% while a list was expected", v); } diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 94ffab175..ee3d86296 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -1280,7 +1280,7 @@ void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v) e->eval(state, env, vTmp); for (auto & i : attrPath) { - state.forceValue(*vAttrs); + state.forceValue(*vAttrs, vAttrs->determinePos(noPos)); Bindings::iterator j; Symbol name = getName(i, state, env); if (vAttrs->type() != nAttrs || @@ -1494,14 +1494,15 @@ void EvalState::incrFunctionCall(ExprLambda * fun) void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res) { - forceValue(fun); + Pos pos = fun.determinePos(noPos); + forceValue(fun, pos); if (fun.type() == nAttrs) { auto found = fun.attrs->find(sFunctor); if (found != fun.attrs->end()) { Value * v = allocValue(); - callFunction(*found->value, fun, *v, noPos); - forceValue(*v); + callFunction(*found->value, fun, *v, pos); + forceValue(*v, pos); return autoCallFunction(args, *v, res); } } @@ -1787,7 +1788,7 @@ void EvalState::forceValueDeep(Value & v) recurse = [&](Value & v) { if (!seen.insert(&v).second) return; - forceValue(v); + forceValue(v, v.determinePos(noPos)); if (v.type() == nAttrs) { for (auto & i : *v.attrs) @@ -1924,7 +1925,7 @@ bool EvalState::isDerivation(Value & v) if (v.type() != nAttrs) return false; Bindings::iterator i = v.attrs->find(sType); if (i == v.attrs->end()) return false; - forceValue(*i->value); + forceValue(*i->value, *i->pos); if (i->value->type() != nString) return false; return strcmp(i->value->string.s, "derivation") == 0; } @@ -2036,8 +2037,8 @@ Path EvalState::coerceToPath(const Pos & pos, Value & v, PathSet & context) bool EvalState::eqValues(Value & v1, Value & v2) { - forceValue(v1); - forceValue(v2); + forceValue(v1, v1.determinePos(noPos)); + forceValue(v2, v2.determinePos(noPos)); /* !!! Hack to support some old broken code that relies on pointer equality tests between sets. (Specifically, builderDefs calls diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 82ce9d1b3..f0b94f440 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -221,7 +221,7 @@ public: of the evaluation of the thunk. If `v' is a delayed function application, call the function and overwrite `v' with the result. Otherwise, this is a no-op. */ - inline void forceValue(Value & v, const Pos & pos = noPos); + inline void forceValue(Value & v, const Pos & pos); /* Force a value, then recursively force list elements and attributes. */ diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc index 25fd9b949..fe1d11505 100644 --- a/src/libexpr/get-drvs.cc +++ b/src/libexpr/get-drvs.cc @@ -172,7 +172,7 @@ StringSet DrvInfo::queryMetaNames() bool DrvInfo::checkMeta(Value & v) { - state->forceValue(v); + state->forceValue(v, v.determinePos(noPos)); if (v.type() == nList) { for (auto elem : v.listItems()) if (!checkMeta(*elem)) return false; @@ -278,7 +278,7 @@ static bool getDerivation(EvalState & state, Value & v, bool ignoreAssertionFailures) { try { - state.forceValue(v); + state.forceValue(v, v.determinePos(noPos)); if (!state.isDerivation(v)) return true; /* Remove spurious duplicates (e.g., a set like `rec { x = -- cgit v1.2.3 From 1472e045a757af4e289801673b456b78ebf18933 Mon Sep 17 00:00:00 2001 From: Kevin Amado Date: Fri, 21 Jan 2022 10:04:09 -0500 Subject: avoid unnecesary calls --- src/libexpr/eval-inline.hh | 16 ---------------- src/libexpr/eval.hh | 2 -- 2 files changed, 18 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/eval-inline.hh b/src/libexpr/eval-inline.hh index cd58315ba..08acb0877 100644 --- a/src/libexpr/eval-inline.hh +++ b/src/libexpr/eval-inline.hh @@ -51,14 +51,6 @@ void EvalState::forceValue(Value & v, const Pos & pos) } -inline void EvalState::forceAttrs(Value & v) -{ - forceValue(v, v.determinePos(noPos)); - if (v.type() != nAttrs) - throwTypeError("value is %1% while a set was expected", v); -} - - inline void EvalState::forceAttrs(Value & v, const Pos & pos) { forceValue(v, pos); @@ -67,14 +59,6 @@ inline void EvalState::forceAttrs(Value & v, const Pos & pos) } -inline void EvalState::forceList(Value & v) -{ - forceValue(v, v.determinePos(noPos)); - if (!v.isList()) - throwTypeError("value is %1% while a list was expected", v); -} - - inline void EvalState::forceList(Value & v, const Pos & pos) { forceValue(v, pos); diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index f0b94f440..11d62ba04 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -231,9 +231,7 @@ public: NixInt forceInt(Value & v, const Pos & pos); NixFloat forceFloat(Value & v, const Pos & pos); bool forceBool(Value & v, const Pos & pos); - inline void forceAttrs(Value & v); inline void forceAttrs(Value & v, const Pos & pos); - inline void forceList(Value & v); inline void forceList(Value & v, const Pos & pos); void forceFunction(Value & v, const Pos & pos); // either lambda or primop string forceString(Value & v, const Pos & pos = noPos); -- cgit v1.2.3 From c3896e19d0001b4f729017aa96d0a44b6e479a52 Mon Sep 17 00:00:00 2001 From: Kevin Amado Date: Fri, 21 Jan 2022 10:43:16 -0500 Subject: forceAttrs: make pos mandatory --- src/libexpr/eval-cache.cc | 2 +- src/libexpr/eval.cc | 2 +- src/libexpr/get-drvs.cc | 2 +- src/libexpr/primops.cc | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/eval-cache.cc b/src/libexpr/eval-cache.cc index a92acafa8..222a34116 100644 --- a/src/libexpr/eval-cache.cc +++ b/src/libexpr/eval-cache.cc @@ -336,7 +336,7 @@ Value & AttrCursor::getValue() if (!_value) { if (parent) { auto & vParent = parent->first->getValue(); - root->state.forceAttrs(vParent); + root->state.forceAttrs(vParent, vParent.determinePos(noPos)); auto attr = vParent.attrs->get(parent->second); if (!attr) throw Error("attribute '%s' is unexpectedly missing", getAttrPathStr()); diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index ee3d86296..e1c089b95 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -1132,7 +1132,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v) Hence we need __overrides.) */ if (hasOverrides) { Value * vOverrides = (*v.attrs)[overrides->second.displ].value; - state.forceAttrs(*vOverrides); + state.forceAttrs(*vOverrides, vOverrides->determinePos(noPos)); Bindings * newBnds = state.allocBindings(v.attrs->capacity() + vOverrides->attrs->size()); for (auto & i : *v.attrs) newBnds->push_back(i); diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc index fe1d11505..d7630fc3c 100644 --- a/src/libexpr/get-drvs.cc +++ b/src/libexpr/get-drvs.cc @@ -107,7 +107,7 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool onlyOutputsToInstall) string name = state->forceStringNoCtx(*elem, *i->pos); Bindings::iterator out = attrs->find(state->symbols.create(name)); if (out == attrs->end()) continue; // FIXME: throw error? - state->forceAttrs(*out->value); + state->forceAttrs(*out->value, *i->pos); /* And evaluate its ‘outPath’ attribute. */ Bindings::iterator outPath = out->value->attrs->find(state->sOutPath); diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 074181e13..9ed1c85c6 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -214,7 +214,7 @@ static void import(EvalState & state, const Pos & pos, Value & vPath, Value * vS if (!vScope) state.evalFile(path, v); else { - state.forceAttrs(*vScope); + state.forceAttrs(*vScope, pos); Env * env = &state.allocEnv(vScope->attrs->size()); env->up = &state.baseEnv; @@ -2485,7 +2485,7 @@ static void prim_zipAttrsWith(EvalState & state, const Pos & pos, Value * * args for (unsigned int n = 0; n < listSize; ++n) { Value * vElem = listElems[n]; try { - state.forceAttrs(*vElem); + state.forceAttrs(*vElem, noPos); for (auto & attr : *vElem->attrs) attrsSeen[attr.name].first++; } catch (TypeError & e) { -- cgit v1.2.3 From 50efc5499a7d924828bed654be207a846c040fa0 Mon Sep 17 00:00:00 2001 From: Kevin Amado Date: Fri, 21 Jan 2022 11:44:19 -0500 Subject: determinePos: remove from critical path --- src/libexpr/attr-path.cc | 2 +- src/libexpr/eval-cache.cc | 4 ++-- src/libexpr/eval.cc | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/attr-path.cc b/src/libexpr/attr-path.cc index 8ec24269f..42a088050 100644 --- a/src/libexpr/attr-path.cc +++ b/src/libexpr/attr-path.cc @@ -58,7 +58,7 @@ std::pair findAlongAttrPath(EvalState & state, const string & attr Value * vNew = state.allocValue(); state.autoCallFunction(autoArgs, *v, *vNew); v = vNew; - state.forceValue(*v, v->determinePos(vIn.determinePos(noPos))); + state.forceValue(*v, noPos); /* It should evaluate to either a set or an expression, according to what is specified in the attrPath. */ diff --git a/src/libexpr/eval-cache.cc b/src/libexpr/eval-cache.cc index 222a34116..d6b9ea29b 100644 --- a/src/libexpr/eval-cache.cc +++ b/src/libexpr/eval-cache.cc @@ -336,7 +336,7 @@ Value & AttrCursor::getValue() if (!_value) { if (parent) { auto & vParent = parent->first->getValue(); - root->state.forceAttrs(vParent, vParent.determinePos(noPos)); + root->state.forceAttrs(vParent, noPos); auto attr = vParent.attrs->get(parent->second); if (!attr) throw Error("attribute '%s' is unexpectedly missing", getAttrPathStr()); @@ -381,7 +381,7 @@ Value & AttrCursor::forceValue() auto & v = getValue(); try { - root->state.forceValue(v, v.determinePos(noPos)); + root->state.forceValue(v, noPos); } catch (EvalError &) { debug("setting '%s' to failed", getAttrPathStr()); if (root->db) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index e1c089b95..df411f3d5 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -1280,7 +1280,7 @@ void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v) e->eval(state, env, vTmp); for (auto & i : attrPath) { - state.forceValue(*vAttrs, vAttrs->determinePos(noPos)); + state.forceValue(*vAttrs, noPos); Bindings::iterator j; Symbol name = getName(i, state, env); if (vAttrs->type() != nAttrs || @@ -2037,8 +2037,8 @@ Path EvalState::coerceToPath(const Pos & pos, Value & v, PathSet & context) bool EvalState::eqValues(Value & v1, Value & v2) { - forceValue(v1, v1.determinePos(noPos)); - forceValue(v2, v2.determinePos(noPos)); + forceValue(v1, noPos); + forceValue(v2, noPos); /* !!! Hack to support some old broken code that relies on pointer equality tests between sets. (Specifically, builderDefs calls -- cgit v1.2.3 From fd5aa6ee3e33a508f3a8ca35e42d36a2437ebacc Mon Sep 17 00:00:00 2001 From: pennae Date: Sat, 22 Jan 2022 21:17:35 +0100 Subject: allocate a GC root value for the Value cache pointer keeping it as a simple data member means it won't be scanned by the GC, so eventually the GC will collect a cache that is still referenced (resulting in use-after-free of cache elements). fixes #5962 --- src/libexpr/eval.cc | 15 ++++++++++----- src/libexpr/eval.hh | 2 +- 2 files changed, 11 insertions(+), 6 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 94ffab175..b884b4001 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -435,6 +435,11 @@ EvalState::EvalState( , store(store) , buildStore(buildStore ? buildStore : store) , regexCache(makeRegexCache()) +#if HAVE_BOEHMGC + , valueAllocCache(std::allocate_shared(traceable_allocator(), nullptr)) +#else + , valueAllocCache(std::make_shared(nullptr)) +#endif , baseEnv(allocEnv(128)) , staticBaseEnv(false, 0) { @@ -852,15 +857,15 @@ Value * EvalState::allocValue() GC_malloc_many returns a linked list of objects of the given size, where the first word of each object is also the pointer to the next object in the list. This also means that we have to explicitly clear the first word of every object we take. */ - if (!valueAllocCache) { - valueAllocCache = GC_malloc_many(sizeof(Value)); - if (!valueAllocCache) throw std::bad_alloc(); + if (!*valueAllocCache) { + *valueAllocCache = GC_malloc_many(sizeof(Value)); + if (!*valueAllocCache) throw std::bad_alloc(); } /* GC_NEXT is a convenience macro for accessing the first word of an object. Take the first list item, advance the list to the next item, and clear the next pointer. */ - void * p = valueAllocCache; - GC_PTR_STORE_AND_DIRTY(&valueAllocCache, GC_NEXT(p)); + void * p = *valueAllocCache; + GC_PTR_STORE_AND_DIRTY(&*valueAllocCache, GC_NEXT(p)); GC_NEXT(p) = nullptr; nrValues++; diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 82ce9d1b3..c59203aa5 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -133,7 +133,7 @@ private: std::shared_ptr regexCache; /* Allocation cache for GC'd Value objects. */ - void * valueAllocCache = nullptr; + std::shared_ptr valueAllocCache; public: -- cgit v1.2.3 From c4fc9b6a8d4fa279206814295729985dac7bc509 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 24 Jan 2022 15:08:16 +0100 Subject: ExprConcatStrings::show(): Print values instead of pointers --- src/libexpr/nixexpr.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/libexpr') diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index 640c44c01..2d2cd96cd 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -191,7 +191,7 @@ void ExprConcatStrings::show(std::ostream & str) const str << "("; for (auto & i : *es) { if (first) first = false; else str << " + "; - str << i.second; + str << *i.second; } str << ")"; } -- cgit v1.2.3 From bed8270c0cbaa3007621ad2f0e722abcefe79702 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 24 Jan 2022 15:18:18 +0100 Subject: Fix parsing of variable names that are a prefix of '__curPos' Fixes $ nix-instantiate --parse -E 'x: with x; _' (x: (with x; __curPos)) --- src/libexpr/parser.y | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/libexpr') diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index a3e713937..55f8abcb7 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -404,7 +404,8 @@ expr_select expr_simple : ID { - if (strncmp($1.p, "__curPos", $1.l) == 0) + std::string_view s = "__curPos"; + if (strncmp($1.p, s.data(), s.size()) == 0) $$ = new ExprPos(CUR_POS); else $$ = new ExprVar(CUR_POS, data->symbols.create($1)); -- cgit v1.2.3 From 8cbbaf23e889270867420431f0ab6c0ffa722335 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 24 Jan 2022 23:02:28 +0100 Subject: Allow builtins.{readFile,path} on invalid paths Stop-gap measure to fix #5975. --- src/libexpr/primops.cc | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 074181e13..9d36f72a8 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1445,9 +1445,13 @@ static void prim_readFile(EvalState & state, const Pos & pos, Value * * args, Va string s = readFile(path); if (s.find((char) 0) != string::npos) throw Error("the contents of the file '%1%' cannot be represented as a Nix string", path); - auto refs = state.store->isInStore(path) ? - state.store->queryPathInfo(state.store->toStorePath(path).first)->references : - StorePathSet{}; + StorePathSet refs; + if (state.store->isInStore(path)) { + try { + refs = state.store->queryPathInfo(state.store->toStorePath(path).first)->references; + } catch (Error &) { // FIXME: should be InvalidPathError + } + } auto context = state.store->printStorePathSet(refs); v.mkString(s, context); } @@ -1866,10 +1870,13 @@ static void addPath( StorePathSet refs; if (state.store->isInStore(path)) { - auto [storePath, subPath] = state.store->toStorePath(path); - // FIXME: we should scanForReferences on the path before adding it - refs = state.store->queryPathInfo(storePath)->references; - path = state.store->toRealPath(storePath) + subPath; + try { + auto [storePath, subPath] = state.store->toStorePath(path); + // FIXME: we should scanForReferences on the path before adding it + refs = state.store->queryPathInfo(storePath)->references; + path = state.store->toRealPath(storePath) + subPath; + } catch (Error &) { // FIXME: should be InvalidPathError + } } path = evalSettings.pureEval && expectedHash -- cgit v1.2.3 From f113ea6c739a9f34fc0c79b1857279b9c0803bf1 Mon Sep 17 00:00:00 2001 From: regnat Date: Tue, 25 Jan 2022 10:49:27 +0100 Subject: Fix parsing of variable names that are a suffix of '__curPos' Follow-up from #5969 Fix #5982 --- src/libexpr/parser.y | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/libexpr') diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 55f8abcb7..7a8e93c12 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -405,7 +405,7 @@ expr_select expr_simple : ID { std::string_view s = "__curPos"; - if (strncmp($1.p, s.data(), s.size()) == 0) + if ($1.l == s.size() && strncmp($1.p, s.data(), s.size()) == 0) $$ = new ExprPos(CUR_POS); else $$ = new ExprVar(CUR_POS, data->symbols.create($1)); -- cgit v1.2.3 From fcdc60ed227ba28f0d0d160d17df27fe179138b0 Mon Sep 17 00:00:00 2001 From: regnat Date: Thu, 27 Jan 2022 15:32:14 +0100 Subject: =?UTF-8?q?Don=E2=80=99t=20require=20`NIX=5FPATH`=20entries=20to?= =?UTF-8?q?=20be=20valid=20paths?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It’s totally valid to have entries in `NIX_PATH` that aren’t valid paths (they can even be arbitrary urls or `channel:`). Fix #5998 and #5980 --- src/libexpr/primops.cc | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 9d36f72a8..acee71d19 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -92,8 +92,6 @@ StringMap EvalState::realiseContext(const PathSet & context) } struct RealisePathFlags { - // Whether to check whether the path is a valid absolute path - bool requireAbsolutePath = true; // Whether to check that the path is allowed in pure eval mode bool checkForPureEval = true; }; @@ -105,9 +103,7 @@ static Path realisePath(EvalState & state, const Pos & pos, Value & v, const Rea auto path = [&]() { try { - return flags.requireAbsolutePath - ? state.coerceToPath(pos, v, context) - : state.coerceToString(pos, v, context, false, false); + return state.coerceToPath(pos, v, context); } catch (Error & e) { e.addTrace(pos, "while realising the context of a path"); throw; @@ -1489,7 +1485,19 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va pos ); - auto path = realisePath(state, pos, *i->value, { .requireAbsolutePath = false }); + PathSet context; + string path = state.coerceToString(pos, *i->value, context, false, false); + + try { + auto rewrites = state.realiseContext(context); + path = rewriteStrings(path, rewrites); + } catch (InvalidPathError & e) { + throw EvalError({ + .msg = hintfmt("cannot find '%1%', since path '%2%' is not valid", path, e.path), + .errPos = pos + }); + } + searchPath.emplace_back(prefix, path); } -- cgit v1.2.3 From 0d7fae6a574ec1b6758a7e6d8e639145c1c465a9 Mon Sep 17 00:00:00 2001 From: pennae Date: Fri, 21 Jan 2022 17:55:51 +0100 Subject: convert a for more utilities to string_view --- src/libexpr/eval.hh | 4 ++-- src/libexpr/parser.y | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index c59203aa5..67bdd4de4 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -309,8 +309,8 @@ private: friend struct ExprAttrs; friend struct ExprLet; - Expr * parse(char * text, size_t length, FileOrigin origin, const Path & path, - const Path & basePath, StaticEnv & staticEnv); + Expr * parse(char * text, size_t length, FileOrigin origin, const PathView path, + const PathView basePath, StaticEnv & staticEnv); public: diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 7a8e93c12..dd76fd66f 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -598,7 +598,7 @@ namespace nix { Expr * EvalState::parse(char * text, size_t length, FileOrigin origin, - const Path & path, const Path & basePath, StaticEnv & staticEnv) + const PathView path, const PathView basePath, StaticEnv & staticEnv) { yyscan_t scanner; ParseData data(*this); -- cgit v1.2.3 From 41d70a2fc8d243d8c83ecc1c9ba648b625957437 Mon Sep 17 00:00:00 2001 From: pennae Date: Fri, 21 Jan 2022 14:44:00 +0100 Subject: return string_views from forceString* once a string has been forced we already have dynamic storage allocated for it, so we can easily reuse that storage instead of copying. --- src/libexpr/attr-path.cc | 2 +- src/libexpr/eval.cc | 12 +++--- src/libexpr/eval.hh | 10 ++--- src/libexpr/flake/flake.cc | 6 +-- src/libexpr/get-drvs.cc | 2 +- src/libexpr/json-to-value.cc | 2 +- src/libexpr/json-to-value.hh | 2 +- src/libexpr/parser.y | 10 ++--- src/libexpr/primops.cc | 81 +++++++++++++++++++---------------- src/libexpr/primops/context.cc | 2 +- src/libexpr/primops/fetchMercurial.cc | 8 ++-- src/libexpr/primops/fromTOML.cc | 2 +- 12 files changed, 73 insertions(+), 66 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/attr-path.cc b/src/libexpr/attr-path.cc index c50c6d92b..edef4d9f8 100644 --- a/src/libexpr/attr-path.cc +++ b/src/libexpr/attr-path.cc @@ -121,7 +121,7 @@ Pos findPackageFilename(EvalState & state, Value & v, std::string what) std::string filename(pos, 0, colon); unsigned int lineno; try { - lineno = std::stoi(std::string(pos, colon + 1)); + lineno = std::stoi(std::string(pos, colon + 1, string::npos)); } catch (std::invalid_argument & e) { throw ParseError("cannot parse line number '%s'", pos); } diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index b884b4001..bfc5d3ebf 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -1857,7 +1857,7 @@ void EvalState::forceFunction(Value & v, const Pos & pos) } -string EvalState::forceString(Value & v, const Pos & pos) +std::string_view EvalState::forceString(Value & v, const Pos & pos) { forceValue(v, pos); if (v.type() != nString) { @@ -1866,7 +1866,7 @@ string EvalState::forceString(Value & v, const Pos & pos) else throwTypeError("value is %1% while a string was expected", v); } - return string(v.string.s); + return v.string.s; } @@ -1901,17 +1901,17 @@ std::vector> Value::getContext() } -string EvalState::forceString(Value & v, PathSet & context, const Pos & pos) +std::string_view EvalState::forceString(Value & v, PathSet & context, const Pos & pos) { - string s = forceString(v, pos); + auto s = forceString(v, pos); copyContext(v, context); return s; } -string EvalState::forceStringNoCtx(Value & v, const Pos & pos) +std::string_view EvalState::forceStringNoCtx(Value & v, const Pos & pos) { - string s = forceString(v, pos); + auto s = forceString(v, pos); if (v.string.context) { if (pos) throwEvalError(pos, "the string '%1%' is not allowed to refer to a store path (such as '%2%')", diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 67bdd4de4..5b94a96ca 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -201,8 +201,8 @@ public: void resetFileCache(); /* Look up a file in the search path. */ - Path findFile(const string & path); - Path findFile(SearchPath & searchPath, const string & path, const Pos & pos = noPos); + Path findFile(const std::string_view path); + Path findFile(SearchPath & searchPath, const std::string_view path, const Pos & pos = noPos); /* If the specified search path element is a URI, download it. */ std::pair resolveSearchPathElem(const SearchPathElem & elem); @@ -236,9 +236,9 @@ public: inline void forceList(Value & v); inline void forceList(Value & v, const Pos & pos); void forceFunction(Value & v, const Pos & pos); // either lambda or primop - string forceString(Value & v, const Pos & pos = noPos); - string forceString(Value & v, PathSet & context, const Pos & pos = noPos); - string forceStringNoCtx(Value & v, const Pos & pos = noPos); + std::string_view forceString(Value & v, const Pos & pos = noPos); + std::string_view forceString(Value & v, PathSet & context, const Pos & pos = noPos); + std::string_view forceStringNoCtx(Value & v, const Pos & pos = noPos); /* Return true iff the value `v' denotes a derivation (i.e. a set with attribute `type = "derivation"'). */ diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc index 0fbe9b960..809f54cc0 100644 --- a/src/libexpr/flake/flake.cc +++ b/src/libexpr/flake/flake.cc @@ -250,7 +250,7 @@ static Flake getFlake( for (auto & setting : *nixConfig->value->attrs) { forceTrivialValue(state, *setting.value, *setting.pos); if (setting.value->type() == nString) - flake.config.settings.insert({setting.name, state.forceStringNoCtx(*setting.value, *setting.pos)}); + flake.config.settings.insert({setting.name, string(state.forceStringNoCtx(*setting.value, *setting.pos))}); else if (setting.value->type() == nPath) { PathSet emptyContext = {}; flake.config.settings.insert({setting.name, state.coerceToString(*setting.pos, *setting.value, emptyContext, false, true, true)}); @@ -265,7 +265,7 @@ static Flake getFlake( if (elem->type() != nString) throw TypeError("list element in flake configuration setting '%s' is %s while a string is expected", setting.name, showType(*setting.value)); - ss.push_back(state.forceStringNoCtx(*elem, *setting.pos)); + ss.emplace_back(state.forceStringNoCtx(*elem, *setting.pos)); } flake.config.settings.insert({setting.name, ss}); } @@ -726,7 +726,7 @@ static void prim_getFlake(EvalState & state, const Pos & pos, Value * * args, Va { state.requireExperimentalFeatureOnEvaluation(Xp::Flakes, "builtins.getFlake", pos); - auto flakeRefS = state.forceStringNoCtx(*args[0], pos); + string flakeRefS(state.forceStringNoCtx(*args[0], pos)); auto flakeRef = parseFlakeRef(flakeRefS, {}, true); if (evalSettings.pureEval && !flakeRef.input.isImmutable()) throw Error("cannot call 'getFlake' on mutable flake reference '%s', at %s (use --impure to override)", flakeRefS, pos); diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc index 25fd9b949..2651266b2 100644 --- a/src/libexpr/get-drvs.cc +++ b/src/libexpr/get-drvs.cc @@ -104,7 +104,7 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool onlyOutputsToInstall) /* For each output... */ for (auto elem : i->value->listItems()) { /* Evaluate the corresponding set. */ - string name = state->forceStringNoCtx(*elem, *i->pos); + string name(state->forceStringNoCtx(*elem, *i->pos)); Bindings::iterator out = attrs->find(state->symbols.create(name)); if (out == attrs->end()) continue; // FIXME: throw error? state->forceAttrs(*out->value); diff --git a/src/libexpr/json-to-value.cc b/src/libexpr/json-to-value.cc index 88716250c..99a475ff9 100644 --- a/src/libexpr/json-to-value.cc +++ b/src/libexpr/json-to-value.cc @@ -163,7 +163,7 @@ public: } }; -void parseJSON(EvalState & state, const string & s_, Value & v) +void parseJSON(EvalState & state, const std::string_view & s_, Value & v) { JSONSax parser(state, v); bool res = json::sax_parse(s_, &parser); diff --git a/src/libexpr/json-to-value.hh b/src/libexpr/json-to-value.hh index 3b0fdae11..84bec4eba 100644 --- a/src/libexpr/json-to-value.hh +++ b/src/libexpr/json-to-value.hh @@ -8,6 +8,6 @@ namespace nix { MakeError(JSONParseError, EvalError); -void parseJSON(EvalState & state, const string & s, Value & v); +void parseJSON(EvalState & state, const std::string_view & s, Value & v); } diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index dd76fd66f..b7910da8c 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -709,24 +709,24 @@ void EvalState::addToSearchPath(const string & s) } -Path EvalState::findFile(const string & path) +Path EvalState::findFile(const std::string_view path) { return findFile(searchPath, path); } -Path EvalState::findFile(SearchPath & searchPath, const string & path, const Pos & pos) +Path EvalState::findFile(SearchPath & searchPath, const std::string_view path, const Pos & pos) { for (auto & i : searchPath) { std::string suffix; if (i.first.empty()) - suffix = "/" + path; + suffix = concatStrings("/", path); else { auto s = i.first.size(); if (path.compare(0, s, i.first) != 0 || (path.size() > s && path[s] != '/')) continue; - suffix = path.size() == s ? "" : "/" + string(path, s); + suffix = path.size() == s ? "" : concatStrings("/", path.substr(s)); } auto r = resolveSearchPathElem(i); if (!r.first) continue; @@ -735,7 +735,7 @@ Path EvalState::findFile(SearchPath & searchPath, const string & path, const Pos } if (hasPrefix(path, "nix/")) - return corepkgsPrefix + path.substr(4); + return concatStrings(corepkgsPrefix, path.substr(4)); throw ThrownError({ .msg = hintfmt(evalSettings.pureEval diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index acee71d19..0454acc3d 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -314,7 +314,7 @@ void prim_importNative(EvalState & state, const Pos & pos, Value * * args, Value { auto path = realisePath(state, pos, *args[0]); - string sym = state.forceStringNoCtx(*args[1], pos); + string sym(state.forceStringNoCtx(*args[1], pos)); void *handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL); if (!handle) @@ -825,7 +825,7 @@ static RegisterPrimOp primop_tryEval({ /* Return an environment variable. Use with care. */ static void prim_getEnv(EvalState & state, const Pos & pos, Value * * args, Value & v) { - string name = state.forceStringNoCtx(*args[0], pos); + string name(state.forceStringNoCtx(*args[0], pos)); v.mkString(evalSettings.restrictEval || evalSettings.pureEval ? "" : getEnv(name).value_or("")); } @@ -975,7 +975,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * const string & key = i->name; vomit("processing attribute '%1%'", key); - auto handleHashMode = [&](const std::string & s) { + auto handleHashMode = [&](const std::string_view s) { if (s == "recursive") ingestionMethod = FileIngestionMethod::Recursive; else if (s == "flat") ingestionMethod = FileIngestionMethod::Flat; else @@ -1502,7 +1502,7 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va searchPath.emplace_back(prefix, path); } - string path = state.forceStringNoCtx(*args[1], pos); + auto path = state.forceStringNoCtx(*args[1], pos); v.mkPath(state.checkSourcePath(state.findFile(searchPath, path, pos))); } @@ -1516,7 +1516,7 @@ static RegisterPrimOp primop_findFile(RegisterPrimOp::Info { /* Return the cryptographic hash of a file in base-16. */ static void prim_hashFile(EvalState & state, const Pos & pos, Value * * args, Value & v) { - string type = state.forceStringNoCtx(*args[0], pos); + auto type = state.forceStringNoCtx(*args[0], pos); std::optional ht = parseHashType(type); if (!ht) throw Error({ @@ -1723,7 +1723,7 @@ static RegisterPrimOp primop_toJSON({ /* Parse a JSON string to a value. */ static void prim_fromJSON(EvalState & state, const Pos & pos, Value * * args, Value & v) { - string s = state.forceStringNoCtx(*args[0], pos); + auto s = state.forceStringNoCtx(*args[0], pos); try { parseJSON(state, s, v); } catch (JSONParseError &e) { @@ -1752,8 +1752,8 @@ static RegisterPrimOp primop_fromJSON({ static void prim_toFile(EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; - string name = state.forceStringNoCtx(*args[0], pos); - string contents = state.forceString(*args[1], context, pos); + string name(state.forceStringNoCtx(*args[0], pos)); + string contents(state.forceString(*args[1], context, pos)); StorePathSet refs; @@ -2153,7 +2153,7 @@ static RegisterPrimOp primop_attrValues({ /* Dynamic version of the `.' operator. */ void prim_getAttr(EvalState & state, const Pos & pos, Value * * args, Value & v) { - string attr = state.forceStringNoCtx(*args[0], pos); + auto attr = state.forceStringNoCtx(*args[0], pos); state.forceAttrs(*args[1], pos); Bindings::iterator i = getAttr( state, @@ -2183,7 +2183,7 @@ static RegisterPrimOp primop_getAttr({ /* Return position information of the specified attribute. */ static void prim_unsafeGetAttrPos(EvalState & state, const Pos & pos, Value * * args, Value & v) { - string attr = state.forceStringNoCtx(*args[0], pos); + auto attr = state.forceStringNoCtx(*args[0], pos); state.forceAttrs(*args[1], pos); Bindings::iterator i = args[1]->attrs->find(state.symbols.create(attr)); if (i == args[1]->attrs->end()) @@ -2201,7 +2201,7 @@ static RegisterPrimOp primop_unsafeGetAttrPos(RegisterPrimOp::Info { /* Dynamic version of the `?' operator. */ static void prim_hasAttr(EvalState & state, const Pos & pos, Value * * args, Value & v) { - string attr = state.forceStringNoCtx(*args[0], pos); + auto attr = state.forceStringNoCtx(*args[0], pos); state.forceAttrs(*args[1], pos); v.mkBool(args[1]->attrs->find(state.symbols.create(attr)) != args[1]->attrs->end()); } @@ -2300,7 +2300,7 @@ static void prim_listToAttrs(EvalState & state, const Pos & pos, Value * * args, pos ); - string name = state.forceStringNoCtx(*j->value, *j->pos); + auto name = state.forceStringNoCtx(*j->value, *j->pos); Symbol sym = state.symbols.create(name); if (seen.insert(sym).second) { @@ -3032,7 +3032,7 @@ static void prim_groupBy(EvalState & state, const Pos & pos, Value * * args, Val for (auto vElem : args[1]->listItems()) { Value res; state.callFunction(*args[0], *vElem, res, pos); - string name = state.forceStringNoCtx(res, pos); + auto name = state.forceStringNoCtx(res, pos); Symbol sym = state.symbols.create(name); auto vector = attrs.try_emplace(sym, ValueVector()).first; vector->second.push_back(vElem); @@ -3376,7 +3376,7 @@ static RegisterPrimOp primop_stringLength({ /* Return the cryptographic hash of a string in base-16. */ static void prim_hashString(EvalState & state, const Pos & pos, Value * * args, Value & v) { - string type = state.forceStringNoCtx(*args[0], pos); + auto type = state.forceStringNoCtx(*args[0], pos); std::optional ht = parseHashType(type); if (!ht) throw Error({ @@ -3385,7 +3385,7 @@ static void prim_hashString(EvalState & state, const Pos & pos, Value * * args, }); PathSet context; // discarded - string s = state.forceString(*args[1], context, pos); + auto s = state.forceString(*args[1], context, pos); v.mkString(hashString(*ht, s).to_string(Base16, false)); } @@ -3403,7 +3403,18 @@ static RegisterPrimOp primop_hashString({ struct RegexCache { - std::unordered_map cache; + // TODO use C++20 transparent comparison when available + std::unordered_map cache; + std::list keys; + + std::regex get(std::string_view re) + { + auto it = cache.find(re); + if (it != cache.end()) + return it->second; + keys.emplace_back(re); + return cache.emplace(keys.back(), std::regex(keys.back(), std::regex::extended)).first->second; + } }; std::shared_ptr makeRegexCache() @@ -3417,15 +3428,13 @@ void prim_match(EvalState & state, const Pos & pos, Value * * args, Value & v) try { - auto regex = state.regexCache->cache.find(re); - if (regex == state.regexCache->cache.end()) - regex = state.regexCache->cache.emplace(re, std::regex(re, std::regex::extended)).first; + auto regex = state.regexCache->get(re); PathSet context; - const std::string str = state.forceString(*args[1], context, pos); + const auto str = state.forceString(*args[1], context, pos); - std::smatch match; - if (!std::regex_match(str, match, regex->second)) { + std::cmatch match; + if (!std::regex_match(str.begin(), str.end(), match, regex)) { v.mkNull(); return; } @@ -3500,15 +3509,13 @@ void prim_split(EvalState & state, const Pos & pos, Value * * args, Value & v) try { - auto regex = state.regexCache->cache.find(re); - if (regex == state.regexCache->cache.end()) - regex = state.regexCache->cache.emplace(re, std::regex(re, std::regex::extended)).first; + auto regex = state.regexCache->get(re); PathSet context; - const std::string str = state.forceString(*args[1], context, pos); + const auto str = state.forceString(*args[1], context, pos); - auto begin = std::sregex_iterator(str.begin(), str.end(), regex->second); - auto end = std::sregex_iterator(); + auto begin = std::cregex_iterator(str.begin(), str.end(), regex); + auto end = std::cregex_iterator(); // Any matches results are surrounded by non-matching results. const size_t len = std::distance(begin, end); @@ -3520,9 +3527,9 @@ void prim_split(EvalState & state, const Pos & pos, Value * * args, Value & v) return; } - for (std::sregex_iterator i = begin; i != end; ++i) { + for (auto i = begin; i != end; ++i) { assert(idx <= 2 * len + 1 - 3); - std::smatch match = *i; + auto match = *i; // Add a string for non-matched characters. (v.listElems()[idx++] = state.allocValue())->mkString(match.prefix().str()); @@ -3643,14 +3650,14 @@ static void prim_replaceStrings(EvalState & state, const Pos & pos, Value * * ar vector from; from.reserve(args[0]->listSize()); for (auto elem : args[0]->listItems()) - from.push_back(state.forceString(*elem, pos)); + from.emplace_back(state.forceString(*elem, pos)); vector> to; to.reserve(args[1]->listSize()); for (auto elem : args[1]->listItems()) { PathSet ctx; auto s = state.forceString(*elem, ctx, pos); - to.push_back(std::make_pair(std::move(s), std::move(ctx))); + to.emplace_back(s, std::move(ctx)); } PathSet context; @@ -3712,7 +3719,7 @@ static RegisterPrimOp primop_replaceStrings({ static void prim_parseDrvName(EvalState & state, const Pos & pos, Value * * args, Value & v) { - string name = state.forceStringNoCtx(*args[0], pos); + auto name = state.forceStringNoCtx(*args[0], pos); DrvName parsed(name); auto attrs = state.buildBindings(2); attrs.alloc(state.sName).mkString(parsed.name); @@ -3736,8 +3743,8 @@ static RegisterPrimOp primop_parseDrvName({ static void prim_compareVersions(EvalState & state, const Pos & pos, Value * * args, Value & v) { - string version1 = state.forceStringNoCtx(*args[0], pos); - string version2 = state.forceStringNoCtx(*args[1], pos); + auto version1 = state.forceStringNoCtx(*args[0], pos); + auto version2 = state.forceStringNoCtx(*args[1], pos); v.mkInt(compareVersions(version1, version2)); } @@ -3756,14 +3763,14 @@ static RegisterPrimOp primop_compareVersions({ static void prim_splitVersion(EvalState & state, const Pos & pos, Value * * args, Value & v) { - string version = state.forceStringNoCtx(*args[0], pos); + auto version = state.forceStringNoCtx(*args[0], pos); auto iter = version.cbegin(); Strings components; while (iter != version.cend()) { auto component = nextComponent(iter, version.cend()); if (component.empty()) break; - components.emplace_back(std::move(component)); + components.emplace_back(component); } state.mkList(v, components.size()); for (const auto & [n, component] : enumerate(components)) diff --git a/src/libexpr/primops/context.cc b/src/libexpr/primops/context.cc index a239c06da..cd7eeb588 100644 --- a/src/libexpr/primops/context.cc +++ b/src/libexpr/primops/context.cc @@ -180,7 +180,7 @@ static void prim_appendContext(EvalState & state, const Pos & pos, Value * * arg } for (auto elem : iter->value->listItems()) { auto name = state.forceStringNoCtx(*elem, *iter->pos); - context.insert("!" + name + "!" + string(i.name)); + context.insert(concatStrings("!", name, "!", i.name)); } } } diff --git a/src/libexpr/primops/fetchMercurial.cc b/src/libexpr/primops/fetchMercurial.cc index 42214c207..f808e2da5 100644 --- a/src/libexpr/primops/fetchMercurial.cc +++ b/src/libexpr/primops/fetchMercurial.cc @@ -12,7 +12,7 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar std::string url; std::optional rev; std::optional ref; - std::string name = "source"; + std::string_view name = "source"; PathSet context; state.forceValue(*args[0], pos); @@ -22,14 +22,14 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar state.forceAttrs(*args[0], pos); for (auto & attr : *args[0]->attrs) { - string n(attr.name); + std::string_view n(attr.name); if (n == "url") url = state.coerceToString(*attr.pos, *attr.value, context, false, false); else if (n == "rev") { // Ugly: unlike fetchGit, here the "rev" attribute can // be both a revision or a branch/tag name. auto value = state.forceStringNoCtx(*attr.value, *attr.pos); - if (std::regex_match(value, revRegex)) + if (std::regex_match(value.begin(), value.end(), revRegex)) rev = Hash::parseAny(value, htSHA1); else ref = value; @@ -62,7 +62,7 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar fetchers::Attrs attrs; attrs.insert_or_assign("type", "hg"); attrs.insert_or_assign("url", url.find("://") != std::string::npos ? url : "file://" + url); - attrs.insert_or_assign("name", name); + attrs.insert_or_assign("name", string(name)); if (ref) attrs.insert_or_assign("ref", *ref); if (rev) attrs.insert_or_assign("rev", rev->gitRev()); auto input = fetchers::Input::fromAttrs(std::move(attrs)); diff --git a/src/libexpr/primops/fromTOML.cc b/src/libexpr/primops/fromTOML.cc index 80c7e0b82..c0e858b61 100644 --- a/src/libexpr/primops/fromTOML.cc +++ b/src/libexpr/primops/fromTOML.cc @@ -9,7 +9,7 @@ static void prim_fromTOML(EvalState & state, const Pos & pos, Value * * args, Va { auto toml = state.forceStringNoCtx(*args[0], pos); - std::istringstream tomlStream(toml); + std::istringstream tomlStream(string{toml}); std::function visit; -- cgit v1.2.3 From d439dceb3bc47f10a6f1f5b8cf4a5b17adc80071 Mon Sep 17 00:00:00 2001 From: pennae Date: Fri, 21 Jan 2022 16:20:54 +0100 Subject: optionally return string_view from coerceToString we'll retain the old coerceToString interface that returns a string, but callers that don't need the returned value to outlive the Value it came from can save copies by using the new interface instead. for values that weren't stringy we'll pass a new buffer argument that'll be used for storage and shouldn't be inspected. --- src/libexpr/eval.cc | 48 +++++++++++++++++++---------------- src/libexpr/eval.hh | 3 ++- src/libexpr/flake/flake.cc | 4 ++- src/libexpr/primops.cc | 38 ++++++++++++++------------- src/libexpr/primops/context.cc | 7 ++--- src/libexpr/primops/fetchMercurial.cc | 4 +-- src/libexpr/primops/fetchTree.cc | 4 +-- 7 files changed, 59 insertions(+), 49 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index bfc5d3ebf..3332dd703 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -1,5 +1,6 @@ #include "eval.hh" #include "hash.hh" +#include "types.hh" #include "util.hh" #include "store-api.hh" #include "derivations.hh" @@ -1694,7 +1695,7 @@ void EvalState::concatLists(Value & v, size_t nrLists, Value * * lists, const Po void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v) { PathSet context; - std::vector s; + std::vector s; size_t sSize = 0; NixInt n = 0; NixFloat nf = 0; @@ -1705,7 +1706,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v) const auto str = [&] { std::string result; result.reserve(sSize); - for (const auto & part : s) result += part; + for (const auto & part : s) result += *part; return result; }; /* c_str() is not str().c_str() because we want to create a string @@ -1715,15 +1716,18 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v) char * result = allocString(sSize + 1); char * tmp = result; for (const auto & part : s) { - memcpy(tmp, part.c_str(), part.size()); - tmp += part.size(); + memcpy(tmp, part->data(), part->size()); + tmp += part->size(); } *tmp = 0; return result; }; + Value values[es->size()]; + Value * vTmpP = values; + for (auto & [i_pos, i] : *es) { - Value vTmp; + Value & vTmp = *vTmpP++; i->eval(state, env, vTmp); /* If the first element is a path, then the result will also @@ -1756,9 +1760,9 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v) /* skip canonization of first path, which would only be not canonized in the first place if it's coming from a ./${foo} type path */ - s.emplace_back( - state.coerceToString(i_pos, vTmp, context, false, firstType == nString, !first)); - sSize += s.back().size(); + auto part = state.coerceToString(i_pos, vTmp, context, false, firstType == nString, !first); + sSize += part->size(); + s.emplace_back(std::move(part)); } first = false; @@ -1942,34 +1946,35 @@ std::optional EvalState::tryAttrsToString(const Pos & pos, Value & v, if (i != v.attrs->end()) { Value v1; callFunction(*i->value, v, v1, pos); - return coerceToString(pos, v1, context, coerceMore, copyToStore); + return coerceToString(pos, v1, context, coerceMore, copyToStore).toOwned(); } return {}; } -string EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context, +BackedStringView EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context, bool coerceMore, bool copyToStore, bool canonicalizePath) { forceValue(v, pos); - string s; - if (v.type() == nString) { copyContext(v, context); - return v.string.s; + return std::string_view(v.string.s); } if (v.type() == nPath) { - Path path(canonicalizePath ? canonPath(v.path) : v.path); - return copyToStore ? copyPathToStore(context, path) : path; + BackedStringView path(PathView(v.path)); + if (canonicalizePath) + path = canonPath(*path); + if (copyToStore) + path = copyPathToStore(context, std::move(path).toOwned()); + return path; } if (v.type() == nAttrs) { auto maybeString = tryAttrsToString(pos, v, context, coerceMore, copyToStore); - if (maybeString) { - return *maybeString; - } + if (maybeString) + return std::move(*maybeString); auto i = v.attrs->find(sOutPath); if (i == v.attrs->end()) throwTypeError(pos, "cannot coerce a set to a string"); return coerceToString(pos, *i->value, context, coerceMore, copyToStore); @@ -1991,14 +1996,13 @@ string EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context, if (v.isList()) { string result; for (auto [n, v2] : enumerate(v.listItems())) { - result += coerceToString(pos, *v2, - context, coerceMore, copyToStore); + result += *coerceToString(pos, *v2, context, coerceMore, copyToStore); if (n < v.listSize() - 1 /* !!! not quite correct */ && (!v2->isList() || v2->listSize() != 0)) result += " "; } - return result; + return std::move(result); } } @@ -2032,7 +2036,7 @@ string EvalState::copyPathToStore(PathSet & context, const Path & path) Path EvalState::coerceToPath(const Pos & pos, Value & v, PathSet & context) { - string path = coerceToString(pos, v, context, false, false); + string path = coerceToString(pos, v, context, false, false).toOwned(); if (path == "" || path[0] != '/') throwEvalError(pos, "string '%1%' doesn't represent an absolute path", path); return path; diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 5b94a96ca..04acc5728 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -1,6 +1,7 @@ #pragma once #include "attr-set.hh" +#include "types.hh" #include "value.hh" #include "nixexpr.hh" #include "symbol-table.hh" @@ -251,7 +252,7 @@ public: string. If `coerceMore' is set, also converts nulls, integers, booleans and lists to a string. If `copyToStore' is set, referenced paths are copied to the Nix store as a side effect. */ - string coerceToString(const Pos & pos, Value & v, PathSet & context, + BackedStringView coerceToString(const Pos & pos, Value & v, PathSet & context, bool coerceMore = false, bool copyToStore = true, bool canonicalizePath = true); diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc index 809f54cc0..27ba3288b 100644 --- a/src/libexpr/flake/flake.cc +++ b/src/libexpr/flake/flake.cc @@ -253,7 +253,9 @@ static Flake getFlake( flake.config.settings.insert({setting.name, string(state.forceStringNoCtx(*setting.value, *setting.pos))}); else if (setting.value->type() == nPath) { PathSet emptyContext = {}; - flake.config.settings.insert({setting.name, state.coerceToString(*setting.pos, *setting.value, emptyContext, false, true, true)}); + flake.config.settings.emplace( + setting.name, + state.coerceToString(*setting.pos, *setting.value, emptyContext, false, true, true) .toOwned()); } else if (setting.value->type() == nInt) flake.config.settings.insert({setting.name, state.forceInt(*setting.value, *setting.pos)}); diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 0454acc3d..b07133d5e 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -350,10 +350,11 @@ void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v) }); } PathSet context; - auto program = state.coerceToString(pos, *elems[0], context, false, false); + auto program = state.coerceToString(pos, *elems[0], context, false, false).toOwned(); Strings commandArgs; - for (unsigned int i = 1; i < args[0]->listSize(); ++i) - commandArgs.emplace_back(state.coerceToString(pos, *elems[i], context, false, false)); + for (unsigned int i = 1; i < args[0]->listSize(); ++i) { + commandArgs.push_back(state.coerceToString(pos, *elems[i], context, false, false).toOwned()); + } try { auto _ = state.realiseContext(context); // FIXME: Handle CA derivations } catch (InvalidPathError & e) { @@ -706,7 +707,7 @@ static RegisterPrimOp primop_abort({ .fun = [](EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; - string s = state.coerceToString(pos, *args[0], context); + string s = state.coerceToString(pos, *args[0], context).toOwned(); throw Abort("evaluation aborted with the following error message: '%1%'", s); } }); @@ -724,7 +725,7 @@ static RegisterPrimOp primop_throw({ .fun = [](EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; - string s = state.coerceToString(pos, *args[0], context); + string s = state.coerceToString(pos, *args[0], context).toOwned(); throw ThrownError(s); } }); @@ -736,7 +737,7 @@ static void prim_addErrorContext(EvalState & state, const Pos & pos, Value * * a v = *args[1]; } catch (Error & e) { PathSet context; - e.addTrace(std::nullopt, state.coerceToString(pos, *args[0], context)); + e.addTrace(std::nullopt, state.coerceToString(pos, *args[0], context).toOwned()); throw; } } @@ -1030,7 +1031,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * else if (i->name == state.sArgs) { state.forceList(*i->value, pos); for (auto elem : i->value->listItems()) { - string s = state.coerceToString(posDrvName, *elem, context, true); + string s = state.coerceToString(posDrvName, *elem, context, true).toOwned(); drv.args.push_back(s); } } @@ -1066,7 +1067,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * } } else { - auto s = state.coerceToString(*i->pos, *i->value, context, true); + auto s = state.coerceToString(*i->pos, *i->value, context, true).toOwned(); drv.env.emplace(key, s); if (i->name == state.sBuilder) drv.builder = std::move(s); else if (i->name == state.sSystem) drv.platform = std::move(s); @@ -1399,7 +1400,7 @@ static RegisterPrimOp primop_pathExists({ static void prim_baseNameOf(EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; - v.mkString(baseNameOf(state.coerceToString(pos, *args[0], context, false, false)), context); + v.mkString(baseNameOf(*state.coerceToString(pos, *args[0], context, false, false)), context); } static RegisterPrimOp primop_baseNameOf({ @@ -1419,7 +1420,8 @@ static RegisterPrimOp primop_baseNameOf({ static void prim_dirOf(EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; - Path dir = dirOf(state.coerceToString(pos, *args[0], context, false, false)); + auto path = state.coerceToString(pos, *args[0], context, false, false); + auto dir = dirOf(*path); if (args[0]->type() == nPath) v.mkPath(dir); else v.mkString(dir, context); } @@ -1486,7 +1488,7 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va ); PathSet context; - string path = state.coerceToString(pos, *i->value, context, false, false); + string path = state.coerceToString(pos, *i->value, context, false, false).toOwned(); try { auto rewrites = state.realiseContext(context); @@ -3288,8 +3290,8 @@ static RegisterPrimOp primop_lessThan({ static void prim_toString(EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; - string s = state.coerceToString(pos, *args[0], context, true, false); - v.mkString(s, context); + auto s = state.coerceToString(pos, *args[0], context, true, false); + v.mkString(*s, context); } static RegisterPrimOp primop_toString({ @@ -3325,7 +3327,7 @@ static void prim_substring(EvalState & state, const Pos & pos, Value * * args, V int start = state.forceInt(*args[0], pos); int len = state.forceInt(*args[1], pos); PathSet context; - string s = state.coerceToString(pos, *args[2], context); + auto s = state.coerceToString(pos, *args[2], context); if (start < 0) throw EvalError({ @@ -3333,7 +3335,7 @@ static void prim_substring(EvalState & state, const Pos & pos, Value * * args, V .errPos = pos }); - v.mkString((unsigned int) start >= s.size() ? "" : string(s, start, len), context); + v.mkString((unsigned int) start >= s->size() ? "" : s->substr(start, len), context); } static RegisterPrimOp primop_substring({ @@ -3359,8 +3361,8 @@ static RegisterPrimOp primop_substring({ static void prim_stringLength(EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; - string s = state.coerceToString(pos, *args[0], context); - v.mkInt(s.size()); + auto s = state.coerceToString(pos, *args[0], context); + v.mkInt(s->size()); } static RegisterPrimOp primop_stringLength({ @@ -3620,7 +3622,7 @@ static void prim_concatStringsSep(EvalState & state, const Pos & pos, Value * * for (auto elem : args[1]->listItems()) { if (first) first = false; else res += sep; - res += state.coerceToString(pos, *elem, context); + res += *state.coerceToString(pos, *elem, context); } v.mkString(res, context); diff --git a/src/libexpr/primops/context.cc b/src/libexpr/primops/context.cc index cd7eeb588..654251c23 100644 --- a/src/libexpr/primops/context.cc +++ b/src/libexpr/primops/context.cc @@ -7,7 +7,8 @@ namespace nix { static void prim_unsafeDiscardStringContext(EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; - v.mkString(state.coerceToString(pos, *args[0], context)); + auto s = state.coerceToString(pos, *args[0], context); + v.mkString(*s); } static RegisterPrimOp primop_unsafeDiscardStringContext("__unsafeDiscardStringContext", 1, prim_unsafeDiscardStringContext); @@ -32,13 +33,13 @@ static RegisterPrimOp primop_hasContext("__hasContext", 1, prim_hasContext); static void prim_unsafeDiscardOutputDependency(EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; - string s = state.coerceToString(pos, *args[0], context); + auto s = state.coerceToString(pos, *args[0], context); PathSet context2; for (auto & p : context) context2.insert(p.at(0) == '=' ? string(p, 1) : p); - v.mkString(s, context2); + v.mkString(*s, context2); } static RegisterPrimOp primop_unsafeDiscardOutputDependency("__unsafeDiscardOutputDependency", 1, prim_unsafeDiscardOutputDependency); diff --git a/src/libexpr/primops/fetchMercurial.cc b/src/libexpr/primops/fetchMercurial.cc index f808e2da5..c4e1a7bf0 100644 --- a/src/libexpr/primops/fetchMercurial.cc +++ b/src/libexpr/primops/fetchMercurial.cc @@ -24,7 +24,7 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar for (auto & attr : *args[0]->attrs) { std::string_view n(attr.name); if (n == "url") - url = state.coerceToString(*attr.pos, *attr.value, context, false, false); + url = state.coerceToString(*attr.pos, *attr.value, context, false, false).toOwned(); else if (n == "rev") { // Ugly: unlike fetchGit, here the "rev" attribute can // be both a revision or a branch/tag name. @@ -50,7 +50,7 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar }); } else - url = state.coerceToString(pos, *args[0], context, false, false); + url = state.coerceToString(pos, *args[0], context, false, false).toOwned(); // FIXME: git externals probably can be used to bypass the URI // whitelist. Ah well. diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index 6647bd35c..d09e2d9e1 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -125,7 +125,7 @@ static void fetchTree( if (attr.name == state.sType) continue; state.forceValue(*attr.value, *attr.pos); if (attr.value->type() == nPath || attr.value->type() == nString) { - auto s = state.coerceToString(*attr.pos, *attr.value, context, false, false); + auto s = state.coerceToString(*attr.pos, *attr.value, context, false, false).toOwned(); attrs.emplace(attr.name, attr.name == "url" ? type == "git" @@ -151,7 +151,7 @@ static void fetchTree( input = fetchers::Input::fromAttrs(std::move(attrs)); } else { - auto url = state.coerceToString(pos, *args[0], context, false, false); + auto url = state.coerceToString(pos, *args[0], context, false, false).toOwned(); if (type == "git") { fetchers::Attrs attrs; -- cgit v1.2.3 From 4bf6af7b555033de5c1d6851edb60a91940d43c3 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 28 Jan 2022 15:10:43 +0100 Subject: Remove a repeated std::move in a for loop --- src/libexpr/primops.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/libexpr') diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index acee71d19..b53425510 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1179,7 +1179,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * drv.outputs.insert_or_assign(i, DerivationOutput { .output = DerivationOutputCAFloating { .method = ingestionMethod, - .hashType = std::move(ht), + .hashType = ht, }, }); } -- cgit v1.2.3 From 85b1427662979c6f4fe765839ff7a742216f70f9 Mon Sep 17 00:00:00 2001 From: Thomas Koch Date: Sun, 30 Jan 2022 10:51:39 +0200 Subject: fix spelling mistakes reported by Debian's lintian tool --- src/libexpr/primops.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index b53425510..ee0ffcd05 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1269,7 +1269,7 @@ static RegisterPrimOp primop_derivationStrict(RegisterPrimOp::Info { substituted by the corresponding output path at build time. For example, 'placeholder "out"' returns the string /1rz4g4znpzjwh1xymhjpm42vipw92pr73vdgl6xs1hycac8kf2n9. At build - time, any occurence of this string in an derivation attribute will + time, any occurrence of this string in an derivation attribute will be replaced with the concrete path in the Nix store of the output ‘out’. */ static void prim_placeholder(EvalState & state, const Pos & pos, Value * * args, Value & v) @@ -2279,7 +2279,7 @@ static RegisterPrimOp primop_removeAttrs({ /* Builds a set from a list specifying (name, value) pairs. To be precise, a list [{name = "name1"; value = value1;} ... {name = "nameN"; value = valueN;}] is transformed to {name1 = value1; - ... nameN = valueN;}. In case of duplicate occurences of the same + ... nameN = valueN;}. In case of duplicate occurrences of the same name, the first takes precedence. */ static void prim_listToAttrs(EvalState & state, const Pos & pos, Value * * args, Value & v) { -- cgit v1.2.3 From 169ea0b83fe09b473766e22337dfd488c96ff2be Mon Sep 17 00:00:00 2001 From: Alexander Bantyev Date: Wed, 2 Feb 2022 21:41:45 +0300 Subject: Flake follows: resolve all follows to absolute It's not possible in general to know in computeLocks, relative to which path the follows was intended to be. So, we always resolve follows to their absolute states when we encounter them (which can either be in parseFlakeInput or computeLocks' fake input population). Fixes https://github.com/NixOS/nix/issues/6013 Fixes https://github.com/NixOS/nix/issues/5609 Fixes https://github.com/NixOS/nix/issues/5697 (again) --- src/libexpr/flake/flake.cc | 87 ++++++++++++++++++---------------------------- 1 file changed, 33 insertions(+), 54 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc index 0fbe9b960..22ff9b153 100644 --- a/src/libexpr/flake/flake.cc +++ b/src/libexpr/flake/flake.cc @@ -89,11 +89,11 @@ static void expectType(EvalState & state, ValueType type, static std::map parseFlakeInputs( EvalState & state, Value * value, const Pos & pos, - const std::optional & baseDir); + const std::optional & baseDir, InputPath lockRootPath); static FlakeInput parseFlakeInput(EvalState & state, const std::string & inputName, Value * value, const Pos & pos, - const std::optional & baseDir) + const std::optional & baseDir, InputPath lockRootPath) { expectType(state, nAttrs, *value, pos); @@ -117,10 +117,12 @@ static FlakeInput parseFlakeInput(EvalState & state, expectType(state, nBool, *attr.value, *attr.pos); input.isFlake = attr.value->boolean; } else if (attr.name == sInputs) { - input.overrides = parseFlakeInputs(state, attr.value, *attr.pos, baseDir); + input.overrides = parseFlakeInputs(state, attr.value, *attr.pos, baseDir, lockRootPath); } else if (attr.name == sFollows) { expectType(state, nString, *attr.value, *attr.pos); - input.follows = parseInputPath(attr.value->string.s); + auto follows(parseInputPath(attr.value->string.s)); + follows.insert(follows.begin(), lockRootPath.begin(), lockRootPath.end()); + input.follows = follows; } else { switch (attr.value->type()) { case nString: @@ -166,7 +168,7 @@ static FlakeInput parseFlakeInput(EvalState & state, static std::map parseFlakeInputs( EvalState & state, Value * value, const Pos & pos, - const std::optional & baseDir) + const std::optional & baseDir, InputPath lockRootPath) { std::map inputs; @@ -178,7 +180,8 @@ static std::map parseFlakeInputs( inputAttr.name, inputAttr.value, *inputAttr.pos, - baseDir)); + baseDir, + lockRootPath)); } return inputs; @@ -188,7 +191,8 @@ static Flake getFlake( EvalState & state, const FlakeRef & originalRef, bool allowLookup, - FlakeCache & flakeCache) + FlakeCache & flakeCache, + InputPath lockRootPath) { auto [sourceInfo, resolvedRef, lockedRef] = fetchOrSubstituteTree( state, originalRef, allowLookup, flakeCache); @@ -223,7 +227,7 @@ static Flake getFlake( auto sInputs = state.symbols.create("inputs"); if (auto inputs = vInfo.attrs->get(sInputs)) - flake.inputs = parseFlakeInputs(state, inputs->value, *inputs->pos, flakeDir); + flake.inputs = parseFlakeInputs(state, inputs->value, *inputs->pos, flakeDir, lockRootPath); auto sOutputs = state.symbols.create("outputs"); @@ -287,6 +291,11 @@ static Flake getFlake( return flake; } +Flake getFlake(EvalState & state, const FlakeRef & originalRef, bool allowLookup, FlakeCache & flakeCache) +{ + return getFlake(state, originalRef, allowLookup, flakeCache, {}); +} + Flake getFlake(EvalState & state, const FlakeRef & originalRef, bool allowLookup) { FlakeCache flakeCache; @@ -332,22 +341,12 @@ LockedFlake lockFlake( std::vector parents; - struct LockParent { - /* The path to this parent. */ - InputPath path; - - /* Whether we are currently inside a top-level lockfile - (inputs absolute) or subordinate lockfile (inputs - relative). */ - bool absolute; - }; - std::function node, const InputPath & inputPathPrefix, std::shared_ptr oldNode, - const LockParent & parent, + const InputPath & lockRootPath, const Path & parentPath, bool trustLock)> computeLocks; @@ -357,7 +356,7 @@ LockedFlake lockFlake( std::shared_ptr node, const InputPath & inputPathPrefix, std::shared_ptr oldNode, - const LockParent & parent, + const InputPath & lockRootPath, const Path & parentPath, bool trustLock) { @@ -402,17 +401,7 @@ LockedFlake lockFlake( if (input.follows) { InputPath target; - if (parent.absolute && !hasOverride) { - target = *input.follows; - } else { - if (hasOverride) { - target = inputPathPrefix; - target.pop_back(); - } else - target = parent.path; - - for (auto & i : *input.follows) target.push_back(i); - } + target.insert(target.end(), input.follows->begin(), input.follows->end()); debug("input '%s' follows '%s'", inputPathS, printInputPath(target)); node->inputs.insert_or_assign(id, target); @@ -485,23 +474,25 @@ LockedFlake lockFlake( break; } } + auto absoluteFollows(lockRootPath); + absoluteFollows.insert(absoluteFollows.end(), follows->begin(), follows->end()); fakeInputs.emplace(i.first, FlakeInput { - .follows = *follows, + .follows = absoluteFollows, }); } } } - LockParent newParent { - .path = inputPath, - .absolute = true - }; - + auto localPath(parentPath); + // If this input is a path, recurse it down. + // This allows us to resolve path inputs relative to the current flake. + if ((*input.ref).input.getType() == "path") + localPath = absPath(*input.ref->input.getSourcePath(), parentPath); computeLocks( mustRefetch - ? getFlake(state, oldLock->lockedRef, false, flakeCache).inputs + ? getFlake(state, oldLock->lockedRef, false, flakeCache, inputPath).inputs : fakeInputs, - childNode, inputPath, oldLock, newParent, parentPath, !mustRefetch); + childNode, inputPath, oldLock, lockRootPath, parentPath, !mustRefetch); } else { /* We need to create a new lock file entry. So fetch @@ -520,7 +511,7 @@ LockedFlake lockFlake( if (localRef.input.getType() == "path") localPath = absPath(*input.ref->input.getSourcePath(), parentPath); - auto inputFlake = getFlake(state, localRef, useRegistries, flakeCache); + auto inputFlake = getFlake(state, localRef, useRegistries, flakeCache, inputPath); /* Note: in case of an --override-input, we use the *original* ref (input2.ref) for the @@ -541,13 +532,6 @@ LockedFlake lockFlake( parents.push_back(*input.ref); Finally cleanup([&]() { parents.pop_back(); }); - // Follows paths from existing inputs in the top-level lockfile are absolute, - // whereas paths in subordinate lockfiles are relative to those lockfiles. - LockParent newParent { - .path = inputPath, - .absolute = oldLock ? true : false - }; - /* Recursively process the inputs of this flake. Also, unless we already have this flake in the top-level lock file, use this flake's @@ -558,7 +542,7 @@ LockedFlake lockFlake( ? std::dynamic_pointer_cast(oldLock) : LockFile::read( inputFlake.sourceInfo->actualPath + "/" + inputFlake.lockedRef.subdir + "/flake.lock").root, - newParent, localPath, false); + oldLock ? lockRootPath : inputPath, localPath, false); } else { @@ -576,17 +560,12 @@ LockedFlake lockFlake( } }; - LockParent parent { - .path = {}, - .absolute = true - }; - // Bring in the current ref for relative path resolution if we have it auto parentPath = canonPath(flake.sourceInfo->actualPath + "/" + flake.lockedRef.subdir, true); computeLocks( flake.inputs, newLockFile.root, {}, - lockFlags.recreateLockFile ? nullptr : oldLockFile.root, parent, parentPath, false); + lockFlags.recreateLockFile ? nullptr : oldLockFile.root, {}, parentPath, false); for (auto & i : lockFlags.inputOverrides) if (!overridesUsed.count(i.first)) -- cgit v1.2.3 From bd383d1b6f91c4fe7ac21c52771e92027f649fa0 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 4 Feb 2022 00:31:33 +0100 Subject: Make most calls to determinePos() lazy --- src/libexpr/eval-inline.hh | 26 +++++++++++++++++--------- src/libexpr/eval.cc | 14 ++++++++++---- src/libexpr/eval.hh | 10 +++++++++- src/libexpr/get-drvs.cc | 4 ++-- src/libexpr/value.hh | 2 +- 5 files changed, 39 insertions(+), 17 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/eval-inline.hh b/src/libexpr/eval-inline.hh index 08acb0877..aef1f6351 100644 --- a/src/libexpr/eval-inline.hh +++ b/src/libexpr/eval-inline.hh @@ -15,12 +15,6 @@ LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s)) }); } -LocalNoInlineNoReturn(void throwTypeError(const char * s, const Value & v)) -{ - throw TypeError(s, showType(v)); -} - - LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const Value & v)) { throw TypeError({ @@ -31,6 +25,13 @@ LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const void EvalState::forceValue(Value & v, const Pos & pos) +{ + forceValue(v, [&]() { return pos; }); +} + + +template +void EvalState::forceValue(Value & v, Callable getPos) { if (v.isThunk()) { Env * env = v.thunk.env; @@ -47,15 +48,22 @@ void EvalState::forceValue(Value & v, const Pos & pos) else if (v.isApp()) callFunction(*v.app.left, *v.app.right, v, noPos); else if (v.isBlackhole()) - throwEvalError(pos, "infinite recursion encountered"); + throwEvalError(getPos(), "infinite recursion encountered"); } inline void EvalState::forceAttrs(Value & v, const Pos & pos) { - forceValue(v, pos); + forceAttrs(v, [&]() { return pos; }); +} + + +template +inline void EvalState::forceAttrs(Value & v, Callable getPos) +{ + forceValue(v, getPos); if (v.type() != nAttrs) - throwTypeError(pos, "value is %1% while a set was expected", v); + throwTypeError(getPos(), "value is %1% while a set was expected", v); } diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index afde29d11..2d4eb57fc 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -219,7 +219,7 @@ string showType(const Value & v) } } -Pos Value::determinePos(const Pos &pos) const +Pos Value::determinePos(const Pos & pos) const { switch (internalType) { case tAttrs: return *attrs->pos; @@ -754,6 +754,11 @@ LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const }); } +LocalNoInlineNoReturn(void throwTypeError(const char * s, const Value & v)) +{ + throw TypeError(s, showType(v)); +} + LocalNoInlineNoReturn(void throwAssertionError(const Pos & pos, const char * s, const string & s1)) { throw AssertionError({ @@ -1138,7 +1143,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v) Hence we need __overrides.) */ if (hasOverrides) { Value * vOverrides = (*v.attrs)[overrides->second.displ].value; - state.forceAttrs(*vOverrides, vOverrides->determinePos(noPos)); + state.forceAttrs(*vOverrides, [&]() { return vOverrides->determinePos(noPos); }); Bindings * newBnds = state.allocBindings(v.attrs->capacity() + vOverrides->attrs->size()); for (auto & i : *v.attrs) newBnds->push_back(i); @@ -1500,7 +1505,8 @@ void EvalState::incrFunctionCall(ExprLambda * fun) void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res) { - Pos pos = fun.determinePos(noPos); + auto pos = fun.determinePos(noPos); + forceValue(fun, pos); if (fun.type() == nAttrs) { @@ -1797,7 +1803,7 @@ void EvalState::forceValueDeep(Value & v) recurse = [&](Value & v) { if (!seen.insert(&v).second) return; - forceValue(v, v.determinePos(noPos)); + forceValue(v, [&]() { return v.determinePos(noPos); }); if (v.type() == nAttrs) { for (auto & i : *v.attrs) diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 190dd16dc..ce5a16e11 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -224,6 +224,9 @@ public: result. Otherwise, this is a no-op. */ inline void forceValue(Value & v, const Pos & pos); + template + inline void forceValue(Value & v, Callable getPos); + /* Force a value, then recursively force list elements and attributes. */ void forceValueDeep(Value & v); @@ -232,7 +235,12 @@ public: NixInt forceInt(Value & v, const Pos & pos); NixFloat forceFloat(Value & v, const Pos & pos); bool forceBool(Value & v, const Pos & pos); - inline void forceAttrs(Value & v, const Pos & pos); + + void forceAttrs(Value & v, const Pos & pos); + + template + inline void forceAttrs(Value & v, Callable getPos); + inline void forceList(Value & v, const Pos & pos); void forceFunction(Value & v, const Pos & pos); // either lambda or primop std::string_view forceString(Value & v, const Pos & pos = noPos); diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc index 1b1cef1bf..5995a857b 100644 --- a/src/libexpr/get-drvs.cc +++ b/src/libexpr/get-drvs.cc @@ -172,7 +172,7 @@ StringSet DrvInfo::queryMetaNames() bool DrvInfo::checkMeta(Value & v) { - state->forceValue(v, v.determinePos(noPos)); + state->forceValue(v, [&]() { return v.determinePos(noPos); }); if (v.type() == nList) { for (auto elem : v.listItems()) if (!checkMeta(*elem)) return false; @@ -278,7 +278,7 @@ static bool getDerivation(EvalState & state, Value & v, bool ignoreAssertionFailures) { try { - state.forceValue(v, v.determinePos(noPos)); + state.forceValue(v, [&]() { return v.determinePos(noPos); }); if (!state.isDerivation(v)) return true; /* Remove spurious duplicates (e.g., a set like `rec { x = diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh index 1896c7563..bef5cd6bd 100644 --- a/src/libexpr/value.hh +++ b/src/libexpr/value.hh @@ -361,7 +361,7 @@ public: return internalType == tList1 ? 1 : internalType == tList2 ? 2 : bigList.size; } - Pos determinePos(const Pos &pos) const; + Pos determinePos(const Pos & pos) const; /* Check whether forcing this value requires a trivial amount of computation. In particular, function applications are -- cgit v1.2.3 From 1daf1babf956cab98857db92de8829a1e7f2ae3e Mon Sep 17 00:00:00 2001 From: pennae Date: Fri, 4 Feb 2022 07:36:56 +0100 Subject: fix nix repl not overriding existing bindings in :a previously :a would override old bindings of a name with new values if the added set contained names that were already bound. in nix 2.6 this doesn't happen any more, which is potentially confusing. fixes #6041 --- src/libexpr/nixexpr.hh | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index 4e923ac89..6f6acb074 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -367,15 +367,19 @@ struct StaticEnv void sort() { - std::sort(vars.begin(), vars.end(), + std::stable_sort(vars.begin(), vars.end(), [](const Vars::value_type & a, const Vars::value_type & b) { return a.first < b.first; }); } void deduplicate() { - const auto last = std::unique(vars.begin(), vars.end(), - [] (const Vars::value_type & a, const Vars::value_type & b) { return a.first == b.first; }); - vars.erase(last, vars.end()); + auto it = vars.begin(), jt = it, end = vars.end(); + while (jt != end) { + *it = *jt++; + while (jt != end && it->first == jt->first) *it = *jt++; + it++; + } + vars.erase(it, end); } Vars::const_iterator find(const Symbol & name) const -- cgit v1.2.3 From afcdc7606cb9c1a1603a3a51b08ac411f82795db Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 21 Feb 2022 16:25:12 +0100 Subject: Remove std::list alias --- src/libexpr/get-drvs.hh | 4 ++-- src/libexpr/primops.cc | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/get-drvs.hh b/src/libexpr/get-drvs.hh index 29bb6a660..128354682 100644 --- a/src/libexpr/get-drvs.hh +++ b/src/libexpr/get-drvs.hh @@ -70,9 +70,9 @@ public: #if HAVE_BOEHMGC -typedef list > DrvInfos; +typedef std::list > DrvInfos; #else -typedef list DrvInfos; +typedef std::list DrvInfos; #endif diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index defb861e6..6f79fc49d 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -574,9 +574,9 @@ struct CompareValues #if HAVE_BOEHMGC -typedef list > ValueList; +typedef std::list > ValueList; #else -typedef list ValueList; +typedef std::list ValueList; #endif -- cgit v1.2.3 From fe9afb65bb35737a144acd612170b2e284298a2f Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 21 Feb 2022 16:28:23 +0100 Subject: Remove std::set alias --- src/libexpr/get-drvs.cc | 2 +- src/libexpr/primops.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc index 5995a857b..167fb5fa9 100644 --- a/src/libexpr/get-drvs.cc +++ b/src/libexpr/get-drvs.cc @@ -266,7 +266,7 @@ void DrvInfo::setMeta(const string & name, Value * v) /* Cache for already considered attrsets. */ -typedef set Done; +typedef std::set Done; /* Evaluate value `v'. If it evaluates to a set of type `derivation', diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 6f79fc49d..44f0b19bc 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -654,7 +654,7 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar // `doneKeys' doesn't need to be a GC root, because its values are // reachable from res. auto cmp = CompareValues(state); - set doneKeys(cmp); + std::set doneKeys(cmp); while (!workSet.empty()) { Value * e = *(workSet.begin()); workSet.pop_front(); -- cgit v1.2.3 From 1ac2664472d0135503e54f0d924a802023855003 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 21 Feb 2022 16:32:34 +0100 Subject: Remove std::vector alias --- src/libexpr/nixexpr.hh | 4 ++-- src/libexpr/parser.y | 16 ++++++++-------- src/libexpr/primops.cc | 4 ++-- 3 files changed, 12 insertions(+), 12 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index 6f6acb074..e7f717eba 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -335,8 +335,8 @@ struct ExprConcatStrings : Expr { Pos pos; bool forceString; - vector > * es; - ExprConcatStrings(const Pos & pos, bool forceString, vector > * es) + std::vector > * es; + ExprConcatStrings(const Pos & pos, bool forceString, std::vector > * es) : pos(pos), forceString(forceString), es(es) { }; COMMON_METHODS }; diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index f0c80ebd5..e1d177c1f 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -193,7 +193,7 @@ static Formals * toFormals(ParseData & data, ParserFormals * formals, static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, - vector > > & es) + std::vector > > & es) { if (es.empty()) return new ExprString(""); @@ -233,7 +233,7 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, } /* Strip spaces from each line. */ - vector > * es2 = new vector >; + std::vector > * es2 = new std::vector >; atStartOfLine = true; size_t curDropped = 0; size_t n = es.size(); @@ -415,7 +415,7 @@ expr_op | expr_op UPDATE expr_op { $$ = new ExprOpUpdate(CUR_POS, $1, $3); } | expr_op '?' attrpath { $$ = new ExprOpHasAttr($1, *$3); } | expr_op '+' expr_op - { $$ = new ExprConcatStrings(CUR_POS, false, new vector >({{makeCurPos(@1, data), $1}, {makeCurPos(@3, data), $3}})); } + { $$ = new ExprConcatStrings(CUR_POS, false, new std::vector >({{makeCurPos(@1, data), $1}, {makeCurPos(@3, data), $3}})); } | expr_op '-' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__sub")), {$1, $3}); } | expr_op '*' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__mul")), {$1, $3}); } | expr_op '/' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__div")), {$1, $3}); } @@ -503,9 +503,9 @@ string_parts_interpolated : string_parts_interpolated STR { $$ = $1; $1->emplace_back(makeCurPos(@2, data), new ExprString(string($2))); } | string_parts_interpolated DOLLAR_CURLY expr '}' { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $3); } - | DOLLAR_CURLY expr '}' { $$ = new vector >; $$->emplace_back(makeCurPos(@1, data), $2); } + | DOLLAR_CURLY expr '}' { $$ = new std::vector >; $$->emplace_back(makeCurPos(@1, data), $2); } | STR DOLLAR_CURLY expr '}' { - $$ = new vector >; + $$ = new std::vector >; $$->emplace_back(makeCurPos(@1, data), new ExprString(string($1))); $$->emplace_back(makeCurPos(@2, data), $3); } @@ -528,7 +528,7 @@ path_start ind_string_parts : ind_string_parts IND_STR { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $2); } | ind_string_parts DOLLAR_CURLY expr '}' { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $3); } - | { $$ = new vector > >; } + | { $$ = new std::vector > >; } ; binds @@ -582,9 +582,9 @@ attrpath } else $$->push_back(AttrName($3)); } - | attr { $$ = new vector; $$->push_back(AttrName(data->symbols.create($1))); } + | attr { $$ = new std::vector; $$->push_back(AttrName(data->symbols.create($1))); } | string_attr - { $$ = new vector; + { $$ = new std::vector; ExprString *str = dynamic_cast($1); if (str) { $$->push_back(AttrName(data->symbols.create(str->s))); diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 44f0b19bc..daf679a5a 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -3649,12 +3649,12 @@ static void prim_replaceStrings(EvalState & state, const Pos & pos, Value * * ar .errPos = pos }); - vector from; + std::vector from; from.reserve(args[0]->listSize()); for (auto elem : args[0]->listItems()) from.emplace_back(state.forceString(*elem, pos)); - vector> to; + std::vector> to; to.reserve(args[1]->listSize()); for (auto elem : args[1]->listItems()) { PathSet ctx; -- cgit v1.2.3 From 36c7b12f3354606a50808197f6695c185fd02893 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 21 Feb 2022 16:37:25 +0100 Subject: Remove std::string alias --- src/libexpr/symbol-table.hh | 8 ++++---- src/libexpr/value.hh | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/symbol-table.hh b/src/libexpr/symbol-table.hh index a090ebae5..48d20c29d 100644 --- a/src/libexpr/symbol-table.hh +++ b/src/libexpr/symbol-table.hh @@ -17,8 +17,8 @@ namespace nix { class Symbol { private: - const string * s; // pointer into SymbolTable - Symbol(const string * s) : s(s) { }; + const std::string * s; // pointer into SymbolTable + Symbol(const std::string * s) : s(s) { }; friend class SymbolTable; public: @@ -72,7 +72,7 @@ class SymbolTable { private: std::unordered_map symbols; - std::list store; + std::list store; public: Symbol create(std::string_view s) @@ -84,7 +84,7 @@ public: auto it = symbols.find(s); if (it != symbols.end()) return it->second; - const string & rawSym = store.emplace_back(s); + auto & rawSym = store.emplace_back(s); return symbols.emplace(rawSym, Symbol(&rawSym)).first->second; } diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh index bef5cd6bd..3fdff71a5 100644 --- a/src/libexpr/value.hh +++ b/src/libexpr/value.hh @@ -77,20 +77,20 @@ class ExternalValueBase public: /* Return a simple string describing the type */ - virtual string showType() const = 0; + virtual std::string showType() const = 0; /* Return a string to be used in builtins.typeOf */ - virtual string typeOf() const = 0; + virtual std::string typeOf() const = 0; /* Coerce the value to a string. Defaults to uncoercable, i.e. throws an - * error + * error. */ - virtual string coerceToString(const Pos & pos, PathSet & context, bool copyMore, bool copyToStore) const; + virtual std::string coerceToString(const Pos & pos, PathSet & context, bool copyMore, bool copyToStore) const; /* Compare to another value of the same type. Defaults to uncomparable, * i.e. always false. */ - virtual bool operator==(const ExternalValueBase & b) const; + virtual bool operator ==(const ExternalValueBase & b) const; /* Print the value as JSON. Defaults to unconvertable, i.e. throws an error */ virtual void printValueAsJSON(EvalState & state, bool strict, -- cgit v1.2.3 From 8518cebfceb36f3744c276cc8dbf432b1275f53f Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 24 Feb 2022 18:09:00 +0100 Subject: libfetchers: Rename immutable -> locked This is more consistent with flake terminology. --- src/libexpr/flake/flake.cc | 8 ++++---- src/libexpr/flake/lockfile.cc | 4 ++-- src/libexpr/primops/fetchTree.cc | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc index 9f3b58909..6e3fa41f7 100644 --- a/src/libexpr/flake/flake.cc +++ b/src/libexpr/flake/flake.cc @@ -501,7 +501,7 @@ LockedFlake lockFlake( this input. */ debug("creating new input '%s'", inputPathS); - if (!lockFlags.allowMutable && !input.ref->input.isImmutable()) + if (!lockFlags.allowMutable && !input.ref->input.isLocked()) throw Error("cannot update flake input '%s' in pure mode", inputPathS); if (input.isFlake) { @@ -650,7 +650,7 @@ LockedFlake lockFlake( now. Corner case: we could have reverted from a dirty to a clean tree! */ if (flake.lockedRef.input == prevLockedRef.input - && !flake.lockedRef.input.isImmutable()) + && !flake.lockedRef.input.isLocked()) throw Error("'%s' did not change after I updated its 'flake.lock' file; is 'flake.lock' under version control?", flake.originalRef); } } else @@ -709,8 +709,8 @@ static void prim_getFlake(EvalState & state, const Pos & pos, Value * * args, Va string flakeRefS(state.forceStringNoCtx(*args[0], pos)); auto flakeRef = parseFlakeRef(flakeRefS, {}, true); - if (evalSettings.pureEval && !flakeRef.input.isImmutable()) - throw Error("cannot call 'getFlake' on mutable flake reference '%s', at %s (use --impure to override)", flakeRefS, pos); + if (evalSettings.pureEval && !flakeRef.input.isLocked()) + throw Error("cannot call 'getFlake' on unlocked flake reference '%s', at %s (use --impure to override)", flakeRefS, pos); callFlake(state, lockFlake(state, flakeRef, diff --git a/src/libexpr/flake/lockfile.cc b/src/libexpr/flake/lockfile.cc index fda340789..60b52d578 100644 --- a/src/libexpr/flake/lockfile.cc +++ b/src/libexpr/flake/lockfile.cc @@ -35,7 +35,7 @@ LockedNode::LockedNode(const nlohmann::json & json) , originalRef(getFlakeRef(json, "original", nullptr)) , isFlake(json.find("flake") != json.end() ? (bool) json["flake"] : true) { - if (!lockedRef.input.isImmutable()) + if (!lockedRef.input.isLocked()) throw Error("lockfile contains mutable lock '%s'", fetchers::attrsToJSON(lockedRef.input.toAttrs())); } @@ -220,7 +220,7 @@ bool LockFile::isImmutable() const for (auto & i : nodes) { if (i == root) continue; auto lockedNode = std::dynamic_pointer_cast(i); - if (lockedNode && !lockedNode->lockedRef.input.isImmutable()) return false; + if (lockedNode && !lockedNode->lockedRef.input.isLocked()) return false; } return true; diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index d09e2d9e1..7496104b6 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -19,7 +19,7 @@ void emitTreeAttrs( bool emptyRevFallback, bool forceDirty) { - assert(input.isImmutable()); + assert(input.isLocked()); auto attrs = state.buildBindings(8); @@ -166,8 +166,8 @@ static void fetchTree( if (!evalSettings.pureEval && !input.isDirect()) input = lookupInRegistries(state.store, input).first; - if (evalSettings.pureEval && !input.isImmutable()) - throw Error("in pure evaluation mode, 'fetchTree' requires an immutable input, at %s", pos); + if (evalSettings.pureEval && !input.isLocked()) + throw Error("in pure evaluation mode, 'fetchTree' requires a locked input, at %s", pos); auto [tree, input2] = input.fetch(state.store); -- cgit v1.2.3 From 9d4697453fc760597befa2e3eb887d2d6d375492 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 25 Feb 2022 09:21:04 +0100 Subject: ExprAttrs::show(): Show attrs in sorted order Issue #6160. --- src/libexpr/nixexpr.cc | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index 2d2cd96cd..27ea0f2ad 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -103,11 +103,18 @@ void ExprAttrs::show(std::ostream & str) const { if (recursive) str << "rec "; str << "{ "; - for (auto & i : attrs) - if (i.second.inherited) - str << "inherit " << i.first << " " << "; "; + typedef const decltype(attrs)::value_type * Attr; + std::vector sorted; + for (auto & i : attrs) sorted.push_back(&i); + std::sort(sorted.begin(), sorted.end(), [](Attr a, Attr b) { + return (const std::string &) a->first < (const std::string &) b->first; + }); + for (auto & i : sorted) { + if (i->second.inherited) + str << "inherit " << i->first << " " << "; "; else - str << i.first << " = " << *i.second.e << "; "; + str << i->first << " = " << *i->second.e << "; "; + } for (auto & i : dynamicAttrs) str << "\"${" << *i.nameExpr << "}\" = " << *i.valueExpr << "; "; str << "}"; -- cgit v1.2.3 From df552ff53e68dff8ca360adbdbea214ece1d08ee Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 25 Feb 2022 16:00:00 +0100 Subject: Remove std::string alias (for real this time) Also use std::string_view in a few more places. --- src/libexpr/attr-path.cc | 6 ++-- src/libexpr/attr-path.hh | 7 ++-- src/libexpr/attr-set.hh | 2 +- src/libexpr/common-eval-args.cc | 8 ++--- src/libexpr/common-eval-args.hh | 2 +- src/libexpr/eval-cache.cc | 2 +- src/libexpr/eval.cc | 66 +++++++++++++++++------------------ src/libexpr/eval.hh | 20 +++++------ src/libexpr/flake/flake.cc | 4 +-- src/libexpr/get-drvs.cc | 40 ++++++++++----------- src/libexpr/get-drvs.hh | 46 ++++++++++++------------ src/libexpr/nixexpr.cc | 14 ++++---- src/libexpr/nixexpr.hh | 19 +++++----- src/libexpr/parser.y | 30 ++++++++-------- src/libexpr/primops.cc | 46 ++++++++++++------------ src/libexpr/primops/context.cc | 10 +++--- src/libexpr/primops/fetchMercurial.cc | 2 +- src/libexpr/primops/fetchTree.cc | 4 +-- src/libexpr/primops/fromTOML.cc | 2 +- src/libexpr/value-to-xml.cc | 2 +- 20 files changed, 169 insertions(+), 163 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/attr-path.cc b/src/libexpr/attr-path.cc index bf0c1dabc..eb0e706c7 100644 --- a/src/libexpr/attr-path.cc +++ b/src/libexpr/attr-path.cc @@ -9,7 +9,7 @@ namespace nix { static Strings parseAttrPath(std::string_view s) { Strings res; - string cur; + std::string cur; auto i = s.begin(); while (i != s.end()) { if (*i == '.') { @@ -41,7 +41,7 @@ std::vector parseAttrPath(EvalState & state, std::string_view s) } -std::pair findAlongAttrPath(EvalState & state, const string & attrPath, +std::pair findAlongAttrPath(EvalState & state, const std::string & attrPath, Bindings & autoArgs, Value & vIn) { Strings tokens = parseAttrPath(attrPath); @@ -121,7 +121,7 @@ Pos findPackageFilename(EvalState & state, Value & v, std::string what) std::string filename(pos, 0, colon); unsigned int lineno; try { - lineno = std::stoi(std::string(pos, colon + 1, string::npos)); + lineno = std::stoi(std::string(pos, colon + 1, std::string::npos)); } catch (std::invalid_argument & e) { throw ParseError("cannot parse line number '%s'", pos); } diff --git a/src/libexpr/attr-path.hh b/src/libexpr/attr-path.hh index 2ee3ea089..ff1135a06 100644 --- a/src/libexpr/attr-path.hh +++ b/src/libexpr/attr-path.hh @@ -10,8 +10,11 @@ namespace nix { MakeError(AttrPathNotFound, Error); MakeError(NoPositionInfo, Error); -std::pair findAlongAttrPath(EvalState & state, const string & attrPath, - Bindings & autoArgs, Value & vIn); +std::pair findAlongAttrPath( + EvalState & state, + const std::string & attrPath, + Bindings & autoArgs, + Value & vIn); /* Heuristic to find the filename and lineno or a nix value. */ Pos findPackageFilename(EvalState & state, Value & v, std::string what); diff --git a/src/libexpr/attr-set.hh b/src/libexpr/attr-set.hh index 3e4899efc..cad9743ea 100644 --- a/src/libexpr/attr-set.hh +++ b/src/libexpr/attr-set.hh @@ -105,7 +105,7 @@ public: for (size_t n = 0; n < size_; n++) res.emplace_back(&attrs[n]); std::sort(res.begin(), res.end(), [](const Attr * a, const Attr * b) { - return (const string &) a->name < (const string &) b->name; + return (const std::string &) a->name < (const std::string &) b->name; }); return res; } diff --git a/src/libexpr/common-eval-args.cc b/src/libexpr/common-eval-args.cc index fffca4ac5..e50ff244c 100644 --- a/src/libexpr/common-eval-args.cc +++ b/src/libexpr/common-eval-args.cc @@ -77,7 +77,7 @@ Bindings * MixEvalArgs::getAutoArgs(EvalState & state) for (auto & i : autoArgs) { auto v = state.allocValue(); if (i.second[0] == 'E') - state.mkThunk_(*v, state.parseExprFromString(string(i.second, 1), absPath("."))); + state.mkThunk_(*v, state.parseExprFromString(i.second.substr(1), absPath("."))); else v->mkString(((std::string_view) i.second).substr(1)); res.insert(state.symbols.create(i.first), v); @@ -85,17 +85,17 @@ Bindings * MixEvalArgs::getAutoArgs(EvalState & state) return res.finish(); } -Path lookupFileArg(EvalState & state, string s) +Path lookupFileArg(EvalState & state, std::string_view s) { if (isUri(s)) { return state.store->toRealPath( fetchers::downloadTarball( state.store, resolveUri(s), "source", false).first.storePath); } else if (s.size() > 2 && s.at(0) == '<' && s.at(s.size() - 1) == '>') { - Path p = s.substr(1, s.size() - 2); + Path p(s.substr(1, s.size() - 2)); return state.findFile(p); } else - return absPath(s); + return absPath(std::string(s)); } } diff --git a/src/libexpr/common-eval-args.hh b/src/libexpr/common-eval-args.hh index 0e113fff1..03fa226aa 100644 --- a/src/libexpr/common-eval-args.hh +++ b/src/libexpr/common-eval-args.hh @@ -22,6 +22,6 @@ private: std::map autoArgs; }; -Path lookupFileArg(EvalState & state, string s); +Path lookupFileArg(EvalState & state, std::string_view s); } diff --git a/src/libexpr/eval-cache.cc b/src/libexpr/eval-cache.cc index d6b9ea29b..00d0749f9 100644 --- a/src/libexpr/eval-cache.cc +++ b/src/libexpr/eval-cache.cc @@ -596,7 +596,7 @@ std::vector AttrCursor::getAttrs() for (auto & attr : *getValue().attrs) attrs.push_back(attr.name); std::sort(attrs.begin(), attrs.end(), [](const Symbol & a, const Symbol & b) { - return (const string &) a < (const string &) b; + return (const std::string &) a < (const std::string &) b; }); if (root->db) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 2d4eb57fc..60f0bf08c 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -174,7 +174,7 @@ std::ostream & operator << (std::ostream & str, const Value & v) } -const Value *getPrimOp(const Value &v) { +const Value * getPrimOp(const Value &v) { const Value * primOp = &v; while (primOp->isPrimOpApp()) { primOp = primOp->primOpApp.left; @@ -183,7 +183,7 @@ const Value *getPrimOp(const Value &v) { return primOp; } -string showType(ValueType type) +std::string_view showType(ValueType type) { switch (type) { case nInt: return "an integer"; @@ -202,20 +202,20 @@ string showType(ValueType type) } -string showType(const Value & v) +std::string showType(const Value & v) { switch (v.internalType) { case tString: return v.string.context ? "a string with context" : "a string"; case tPrimOp: - return fmt("the built-in function '%s'", string(v.primOp->name)); + return fmt("the built-in function '%s'", std::string(v.primOp->name)); case tPrimOpApp: - return fmt("the partially applied built-in function '%s'", string(getPrimOp(v)->primOp->name)); + return fmt("the partially applied built-in function '%s'", std::string(getPrimOp(v)->primOp->name)); case tExternal: return v.external->showType(); case tThunk: return "a thunk"; case tApp: return "a function application"; case tBlackhole: return "a black hole"; default: - return showType(v.type()); + return std::string(showType(v.type())); } } @@ -356,7 +356,7 @@ void initGC() /* Very hacky way to parse $NIX_PATH, which is colon-separated, but can contain URLs (e.g. "nixpkgs=https://bla...:foo=https://"). */ -static Strings parseNixPath(const string & s) +static Strings parseNixPath(const std::string & s) { Strings res; @@ -606,7 +606,7 @@ Path EvalState::toRealPath(const Path & path, const PathSet & context) } -Value * EvalState::addConstant(const string & name, Value & v) +Value * EvalState::addConstant(const std::string & name, Value & v) { Value * v2 = allocValue(); *v2 = v; @@ -615,19 +615,19 @@ Value * EvalState::addConstant(const string & name, Value & v) } -void EvalState::addConstant(const string & name, Value * v) +void EvalState::addConstant(const std::string & name, Value * v) { staticBaseEnv.vars.emplace_back(symbols.create(name), baseEnvDispl); baseEnv.values[baseEnvDispl++] = v; - string name2 = string(name, 0, 2) == "__" ? string(name, 2) : name; + auto name2 = name.substr(0, 2) == "__" ? name.substr(2) : name; baseEnv.values[0]->attrs->push_back(Attr(symbols.create(name2), v)); } -Value * EvalState::addPrimOp(const string & name, +Value * EvalState::addPrimOp(const std::string & name, size_t arity, PrimOpFun primOp) { - auto name2 = string(name, 0, 2) == "__" ? string(name, 2) : name; + auto name2 = name.substr(0, 2) == "__" ? name.substr(2) : name; Symbol sym = symbols.create(name2); /* Hack to make constants lazy: turn them into a application of @@ -675,7 +675,7 @@ Value * EvalState::addPrimOp(PrimOp && primOp) } -Value & EvalState::getBuiltin(const string & name) +Value & EvalState::getBuiltin(const std::string & name) { return *baseEnv.values[0]->attrs->find(symbols.create(name))->value; } @@ -703,12 +703,12 @@ std::optional EvalState::getDoc(Value & v) evaluator. So here are some helper functions for throwing exceptions. */ -LocalNoInlineNoReturn(void throwEvalError(const char * s, const string & s2)) +LocalNoInlineNoReturn(void throwEvalError(const char * s, const std::string & s2)) { throw EvalError(s, s2); } -LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const string & s2)) +LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const std::string & s2)) { throw EvalError({ .msg = hintfmt(s, s2), @@ -716,12 +716,12 @@ LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const }); } -LocalNoInlineNoReturn(void throwEvalError(const char * s, const string & s2, const string & s3)) +LocalNoInlineNoReturn(void throwEvalError(const char * s, const std::string & s2, const std::string & s3)) { throw EvalError(s, s2, s3); } -LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const string & s2, const string & s3)) +LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const std::string & s2, const std::string & s3)) { throw EvalError({ .msg = hintfmt(s, s2, s3), @@ -759,7 +759,7 @@ LocalNoInlineNoReturn(void throwTypeError(const char * s, const Value & v)) throw TypeError(s, showType(v)); } -LocalNoInlineNoReturn(void throwAssertionError(const Pos & pos, const char * s, const string & s1)) +LocalNoInlineNoReturn(void throwAssertionError(const Pos & pos, const char * s, const std::string & s1)) { throw AssertionError({ .msg = hintfmt(s, s1), @@ -767,7 +767,7 @@ LocalNoInlineNoReturn(void throwAssertionError(const Pos & pos, const char * s, }); } -LocalNoInlineNoReturn(void throwUndefinedVarError(const Pos & pos, const char * s, const string & s1)) +LocalNoInlineNoReturn(void throwUndefinedVarError(const Pos & pos, const char * s, const std::string & s1)) { throw UndefinedVarError({ .msg = hintfmt(s, s1), @@ -775,7 +775,7 @@ LocalNoInlineNoReturn(void throwUndefinedVarError(const Pos & pos, const char * }); } -LocalNoInlineNoReturn(void throwMissingArgumentError(const Pos & pos, const char * s, const string & s1)) +LocalNoInlineNoReturn(void throwMissingArgumentError(const Pos & pos, const char * s, const std::string & s1)) { throw MissingArgumentError({ .msg = hintfmt(s, s1), @@ -783,12 +783,12 @@ LocalNoInlineNoReturn(void throwMissingArgumentError(const Pos & pos, const char }); } -LocalNoInline(void addErrorTrace(Error & e, const char * s, const string & s2)) +LocalNoInline(void addErrorTrace(Error & e, const char * s, const std::string & s2)) { e.addTrace(std::nullopt, s, s2); } -LocalNoInline(void addErrorTrace(Error & e, const Pos & pos, const char * s, const string & s2)) +LocalNoInline(void addErrorTrace(Error & e, const Pos & pos, const char * s, const std::string & s2)) { e.addTrace(pos, s, s2); } @@ -1221,7 +1221,7 @@ void ExprVar::eval(EvalState & state, Env & env, Value & v) } -static string showAttrPath(EvalState & state, Env & env, const AttrPath & attrPath) +static std::string showAttrPath(EvalState & state, Env & env, const AttrPath & attrPath) { std::ostringstream out; bool first = true; @@ -1395,7 +1395,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & if (loggerSettings.showTrace.get()) { addErrorTrace(e, lambda.pos, "while evaluating %s", (lambda.name.set() - ? "'" + (string) lambda.name + "'" + ? "'" + (const std::string &) lambda.name + "'" : "anonymous lambda")); addErrorTrace(e, pos, "from call site%s", ""); } @@ -1883,7 +1883,7 @@ std::string_view EvalState::forceString(Value & v, const Pos & pos) /* Decode a context string ‘!!’ into a pair . */ -std::pair decodeContext(std::string_view s) +std::pair decodeContext(std::string_view s) { if (s.at(0) == '!') { size_t index = s.find("!", 1); @@ -1946,7 +1946,7 @@ bool EvalState::isDerivation(Value & v) } -std::optional EvalState::tryAttrsToString(const Pos & pos, Value & v, +std::optional EvalState::tryAttrsToString(const Pos & pos, Value & v, PathSet & context, bool coerceMore, bool copyToStore) { auto i = v.attrs->find(sToString); @@ -2001,7 +2001,7 @@ BackedStringView EvalState::coerceToString(const Pos & pos, Value & v, PathSet & if (v.type() == nNull) return ""; if (v.isList()) { - string result; + std::string result; for (auto [n, v2] : enumerate(v.listItems())) { result += *coerceToString(pos, *v2, context, coerceMore, copyToStore); if (n < v.listSize() - 1 @@ -2017,7 +2017,7 @@ BackedStringView EvalState::coerceToString(const Pos & pos, Value & v, PathSet & } -string EvalState::copyPathToStore(PathSet & context, const Path & path) +std::string EvalState::copyPathToStore(PathSet & context, const Path & path) { if (nix::isDerivation(path)) throwEvalError("file names are not allowed to end in '%1%'", drvExtension); @@ -2043,7 +2043,7 @@ string EvalState::copyPathToStore(PathSet & context, const Path & path) Path EvalState::coerceToPath(const Pos & pos, Value & v, PathSet & context) { - string path = coerceToString(pos, v, context, false, false).toOwned(); + auto path = coerceToString(pos, v, context, false, false).toOwned(); if (path == "" || path[0] != '/') throwEvalError(pos, "string '%1%' doesn't represent an absolute path", path); return path; @@ -2213,11 +2213,11 @@ void EvalState::printStats() for (auto & i : functionCalls) { auto obj = list.object(); if (i.first->name.set()) - obj.attr("name", (const string &) i.first->name); + obj.attr("name", (const std::string &) i.first->name); else obj.attr("name", nullptr); if (i.first->pos) { - obj.attr("file", (const string &) i.first->pos.file); + obj.attr("file", (const std::string &) i.first->pos.file); obj.attr("line", i.first->pos.line); obj.attr("column", i.first->pos.column); } @@ -2229,7 +2229,7 @@ void EvalState::printStats() for (auto & i : attrSelects) { auto obj = list.object(); if (i.first) { - obj.attr("file", (const string &) i.first.file); + obj.attr("file", (const std::string &) i.first.file); obj.attr("line", i.first.line); obj.attr("column", i.first.column); } @@ -2246,7 +2246,7 @@ void EvalState::printStats() } -string ExternalValueBase::coerceToString(const Pos & pos, PathSet & context, bool copyMore, bool copyToStore) const +std::string ExternalValueBase::coerceToString(const Pos & pos, PathSet & context, bool copyMore, bool copyToStore) const { throw TypeError({ .msg = hintfmt("cannot coerce %1% to a string", showType()), diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index ce5a16e11..1f0e97b2e 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -150,7 +150,7 @@ public: const Pos & pos ); - void addToSearchPath(const string & s); + void addToSearchPath(const std::string & s); SearchPath getSearchPath() { return searchPath; } @@ -251,7 +251,7 @@ public: set with attribute `type = "derivation"'). */ bool isDerivation(Value & v); - std::optional tryAttrsToString(const Pos & pos, Value & v, + std::optional tryAttrsToString(const Pos & pos, Value & v, PathSet & context, bool coerceMore = false, bool copyToStore = true); /* String coercion. Converts strings, paths and derivations to a @@ -262,7 +262,7 @@ public: bool coerceMore = false, bool copyToStore = true, bool canonicalizePath = true); - string copyPathToStore(PathSet & context, const Path & path); + std::string copyPathToStore(PathSet & context, const Path & path); /* Path coercion. Converts strings, paths and derivations to a path. The result is guaranteed to be a canonicalised, absolute @@ -284,18 +284,18 @@ private: void createBaseEnv(); - Value * addConstant(const string & name, Value & v); + Value * addConstant(const std::string & name, Value & v); - void addConstant(const string & name, Value * v); + void addConstant(const std::string & name, Value * v); - Value * addPrimOp(const string & name, + Value * addPrimOp(const std::string & name, size_t arity, PrimOpFun primOp); Value * addPrimOp(PrimOp && primOp); public: - Value & getBuiltin(const string & name); + Value & getBuiltin(const std::string & name); struct Doc { @@ -414,12 +414,12 @@ private: /* Return a string representing the type of the value `v'. */ -string showType(ValueType type); -string showType(const Value & v); +std::string_view showType(ValueType type); +std::string showType(const Value & v); /* Decode a context string ‘!!’ into a pair . */ -std::pair decodeContext(std::string_view s); +std::pair decodeContext(std::string_view s); /* If `path' refers to a directory, then append "/default.nix". */ Path resolveExprPath(Path path); diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc index 6e3fa41f7..ad0881641 100644 --- a/src/libexpr/flake/flake.cc +++ b/src/libexpr/flake/flake.cc @@ -254,7 +254,7 @@ static Flake getFlake( for (auto & setting : *nixConfig->value->attrs) { forceTrivialValue(state, *setting.value, *setting.pos); if (setting.value->type() == nString) - flake.config.settings.insert({setting.name, string(state.forceStringNoCtx(*setting.value, *setting.pos))}); + flake.config.settings.insert({setting.name, std::string(state.forceStringNoCtx(*setting.value, *setting.pos))}); else if (setting.value->type() == nPath) { PathSet emptyContext = {}; flake.config.settings.emplace( @@ -707,7 +707,7 @@ static void prim_getFlake(EvalState & state, const Pos & pos, Value * * args, Va { state.requireExperimentalFeatureOnEvaluation(Xp::Flakes, "builtins.getFlake", pos); - string flakeRefS(state.forceStringNoCtx(*args[0], pos)); + std::string flakeRefS(state.forceStringNoCtx(*args[0], pos)); auto flakeRef = parseFlakeRef(flakeRefS, {}, true); if (evalSettings.pureEval && !flakeRef.input.isLocked()) throw Error("cannot call 'getFlake' on unlocked flake reference '%s', at %s (use --impure to override)", flakeRefS, pos); diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc index 167fb5fa9..8393e4225 100644 --- a/src/libexpr/get-drvs.cc +++ b/src/libexpr/get-drvs.cc @@ -11,8 +11,8 @@ namespace nix { -DrvInfo::DrvInfo(EvalState & state, const string & attrPath, Bindings * attrs) - : state(&state), attrs(attrs), attrPath(attrPath) +DrvInfo::DrvInfo(EvalState & state, std::string attrPath, Bindings * attrs) + : state(&state), attrs(attrs), attrPath(std::move(attrPath)) { } @@ -47,7 +47,7 @@ DrvInfo::DrvInfo(EvalState & state, ref store, const std::string & drvPat } -string DrvInfo::queryName() const +std::string DrvInfo::queryName() const { if (name == "" && attrs) { auto i = attrs->find(state->sName); @@ -58,7 +58,7 @@ string DrvInfo::queryName() const } -string DrvInfo::querySystem() const +std::string DrvInfo::querySystem() const { if (system == "" && attrs) { auto i = attrs->find(state->sSystem); @@ -68,7 +68,7 @@ string DrvInfo::querySystem() const } -string DrvInfo::queryDrvPath() const +std::string DrvInfo::queryDrvPath() const { if (drvPath == "" && attrs) { Bindings::iterator i = attrs->find(state->sDrvPath); @@ -79,7 +79,7 @@ string DrvInfo::queryDrvPath() const } -string DrvInfo::queryOutPath() const +std::string DrvInfo::queryOutPath() const { if (!outPath && attrs) { Bindings::iterator i = attrs->find(state->sOutPath); @@ -104,7 +104,7 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool onlyOutputsToInstall) /* For each output... */ for (auto elem : i->value->listItems()) { /* Evaluate the corresponding set. */ - string name(state->forceStringNoCtx(*elem, *i->pos)); + std::string name(state->forceStringNoCtx(*elem, *i->pos)); Bindings::iterator out = attrs->find(state->symbols.create(name)); if (out == attrs->end()) continue; // FIXME: throw error? state->forceAttrs(*out->value, *i->pos); @@ -138,7 +138,7 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool onlyOutputsToInstall) } -string DrvInfo::queryOutputName() const +std::string DrvInfo::queryOutputName() const { if (outputName == "" && attrs) { Bindings::iterator i = attrs->find(state->sOutputName); @@ -190,7 +190,7 @@ bool DrvInfo::checkMeta(Value & v) } -Value * DrvInfo::queryMeta(const string & name) +Value * DrvInfo::queryMeta(const std::string & name) { if (!getMeta()) return 0; Bindings::iterator a = meta->find(state->symbols.create(name)); @@ -199,7 +199,7 @@ Value * DrvInfo::queryMeta(const string & name) } -string DrvInfo::queryMetaString(const string & name) +std::string DrvInfo::queryMetaString(const std::string & name) { Value * v = queryMeta(name); if (!v || v->type() != nString) return ""; @@ -207,7 +207,7 @@ string DrvInfo::queryMetaString(const string & name) } -NixInt DrvInfo::queryMetaInt(const string & name, NixInt def) +NixInt DrvInfo::queryMetaInt(const std::string & name, NixInt def) { Value * v = queryMeta(name); if (!v) return def; @@ -221,7 +221,7 @@ NixInt DrvInfo::queryMetaInt(const string & name, NixInt def) return def; } -NixFloat DrvInfo::queryMetaFloat(const string & name, NixFloat def) +NixFloat DrvInfo::queryMetaFloat(const std::string & name, NixFloat def) { Value * v = queryMeta(name); if (!v) return def; @@ -236,7 +236,7 @@ NixFloat DrvInfo::queryMetaFloat(const string & name, NixFloat def) } -bool DrvInfo::queryMetaBool(const string & name, bool def) +bool DrvInfo::queryMetaBool(const std::string & name, bool def) { Value * v = queryMeta(name); if (!v) return def; @@ -251,7 +251,7 @@ bool DrvInfo::queryMetaBool(const string & name, bool def) } -void DrvInfo::setMeta(const string & name, Value * v) +void DrvInfo::setMeta(const std::string & name, Value * v) { getMeta(); auto attrs = state->buildBindings(1 + (meta ? meta->size() : 0)); @@ -274,7 +274,7 @@ typedef std::set Done; The result boolean indicates whether it makes sense for the caller to recursively search for derivations in `v'. */ static bool getDerivation(EvalState & state, Value & v, - const string & attrPath, DrvInfos & drvs, Done & done, + const std::string & attrPath, DrvInfos & drvs, Done & done, bool ignoreAssertionFailures) { try { @@ -311,7 +311,7 @@ std::optional getDerivation(EvalState & state, Value & v, } -static string addToPath(const string & s1, const string & s2) +static std::string addToPath(const std::string & s1, const std::string & s2) { return s1.empty() ? s2 : s1 + "." + s2; } @@ -321,7 +321,7 @@ static std::regex attrRegex("[A-Za-z_][A-Za-z0-9-_+]*"); static void getDerivations(EvalState & state, Value & vIn, - const string & pathPrefix, Bindings & autoArgs, + const std::string & pathPrefix, Bindings & autoArgs, DrvInfos & drvs, Done & done, bool ignoreAssertionFailures) { @@ -346,7 +346,7 @@ static void getDerivations(EvalState & state, Value & vIn, debug("evaluating attribute '%1%'", i->name); if (!std::regex_match(std::string(i->name), attrRegex)) continue; - string pathPrefix2 = addToPath(pathPrefix, i->name); + std::string pathPrefix2 = addToPath(pathPrefix, i->name); if (combineChannels) getDerivations(state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures); else if (getDerivation(state, *i->value, pathPrefix2, drvs, done, ignoreAssertionFailures)) { @@ -364,7 +364,7 @@ static void getDerivations(EvalState & state, Value & vIn, else if (v.type() == nList) { for (auto [n, elem] : enumerate(v.listItems())) { - string pathPrefix2 = addToPath(pathPrefix, fmt("%d", n)); + std::string pathPrefix2 = addToPath(pathPrefix, fmt("%d", n)); if (getDerivation(state, *elem, pathPrefix2, drvs, done, ignoreAssertionFailures)) getDerivations(state, *elem, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures); } @@ -374,7 +374,7 @@ static void getDerivations(EvalState & state, Value & vIn, } -void getDerivations(EvalState & state, Value & v, const string & pathPrefix, +void getDerivations(EvalState & state, Value & v, const std::string & pathPrefix, Bindings & autoArgs, DrvInfos & drvs, bool ignoreAssertionFailures) { Done done; diff --git a/src/libexpr/get-drvs.hh b/src/libexpr/get-drvs.hh index 128354682..d13847785 100644 --- a/src/libexpr/get-drvs.hh +++ b/src/libexpr/get-drvs.hh @@ -12,16 +12,16 @@ namespace nix { struct DrvInfo { public: - typedef std::map Outputs; + typedef std::map Outputs; private: EvalState * state; - mutable string name; - mutable string system; - mutable string drvPath; - mutable std::optional outPath; - mutable string outputName; + mutable std::string name; + mutable std::string system; + mutable std::string drvPath; + mutable std::optional outPath; + mutable std::string outputName; Outputs outputs; bool failed = false; // set if we get an AssertionError @@ -33,36 +33,36 @@ private: bool checkMeta(Value & v); public: - string attrPath; /* path towards the derivation */ + std::string attrPath; /* path towards the derivation */ DrvInfo(EvalState & state) : state(&state) { }; - DrvInfo(EvalState & state, const string & attrPath, Bindings * attrs); + DrvInfo(EvalState & state, std::string attrPath, Bindings * attrs); DrvInfo(EvalState & state, ref store, const std::string & drvPathWithOutputs); - string queryName() const; - string querySystem() const; - string queryDrvPath() const; - string queryOutPath() const; - string queryOutputName() const; + std::string queryName() const; + std::string querySystem() const; + std::string queryDrvPath() const; + std::string queryOutPath() const; + std::string queryOutputName() const; /** Return the list of outputs. The "outputs to install" are determined by `meta.outputsToInstall`. */ Outputs queryOutputs(bool onlyOutputsToInstall = false); StringSet queryMetaNames(); - Value * queryMeta(const string & name); - string queryMetaString(const string & name); - NixInt queryMetaInt(const string & name, NixInt def); - NixFloat queryMetaFloat(const string & name, NixFloat def); - bool queryMetaBool(const string & name, bool def); - void setMeta(const string & name, Value * v); + Value * queryMeta(const std::string & name); + std::string queryMetaString(const std::string & name); + NixInt queryMetaInt(const std::string & name, NixInt def); + NixFloat queryMetaFloat(const std::string & name, NixFloat def); + bool queryMetaBool(const std::string & name, bool def); + void setMeta(const std::string & name, Value * v); /* MetaInfo queryMetaInfo(EvalState & state) const; MetaValue queryMetaInfo(EvalState & state, const string & name) const; */ - void setName(const string & s) { name = s; } - void setDrvPath(const string & s) { drvPath = s; } - void setOutPath(const string & s) { outPath = s; } + void setName(const std::string & s) { name = s; } + void setDrvPath(const std::string & s) { drvPath = s; } + void setOutPath(const std::string & s) { outPath = s; } void setFailed() { failed = true; }; bool hasFailed() { return failed; }; @@ -81,7 +81,7 @@ typedef std::list DrvInfos; std::optional getDerivation(EvalState & state, Value & v, bool ignoreAssertionFailures); -void getDerivations(EvalState & state, Value & v, const string & pathPrefix, +void getDerivations(EvalState & state, Value & v, const std::string & pathPrefix, Bindings & autoArgs, DrvInfos & drvs, bool ignoreAssertionFailures); diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index 27ea0f2ad..a2def65a6 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -16,10 +16,10 @@ std::ostream & operator << (std::ostream & str, const Expr & e) return str; } -static void showString(std::ostream & str, const string & s) +static void showString(std::ostream & str, std::string_view s) { str << '"'; - for (auto c : (string) s) + for (auto c : s) if (c == '"' || c == '\\' || c == '$') str << "\\" << c; else if (c == '\n') str << "\\n"; else if (c == '\r') str << "\\r"; @@ -28,7 +28,7 @@ static void showString(std::ostream & str, const string & s) str << '"'; } -static void showId(std::ostream & str, const string & s) +static void showId(std::ostream & str, std::string_view s) { if (s.empty()) str << "\"\""; @@ -218,7 +218,7 @@ std::ostream & operator << (std::ostream & str, const Pos & pos) auto f = format(ANSI_BOLD "%1%" ANSI_NORMAL ":%2%:%3%"); switch (pos.origin) { case foFile: - f % (string) pos.file; + f % (const std::string &) pos.file; break; case foStdin: case foString: @@ -234,7 +234,7 @@ std::ostream & operator << (std::ostream & str, const Pos & pos) } -string showAttrPath(const AttrPath & attrPath) +std::string showAttrPath(const AttrPath & attrPath) { std::ostringstream out; bool first = true; @@ -468,9 +468,9 @@ void ExprLambda::setName(Symbol & name) } -string ExprLambda::showNamePos() const +std::string ExprLambda::showNamePos() const { - return (format("%1% at %2%") % (name.set() ? "'" + (string) name + "'" : "anonymous function") % pos).str(); + return fmt("%1% at %2%", name.set() ? "'" + (std::string) name + "'" : "anonymous function", pos); } diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index e7f717eba..12b54b8eb 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -26,18 +26,21 @@ struct Pos FileOrigin origin; Symbol file; unsigned int line, column; - Pos() : origin(foString), line(0), column(0) { }; + + Pos() : origin(foString), line(0), column(0) { } Pos(FileOrigin origin, const Symbol & file, unsigned int line, unsigned int column) - : origin(origin), file(file), line(line), column(column) { }; + : origin(origin), file(file), line(line), column(column) { } + operator bool() const { return line != 0; } + bool operator < (const Pos & p2) const { if (!line) return p2.line; if (!p2.line) return false; - int d = ((string) file).compare((string) p2.file); + int d = ((const std::string &) file).compare((const std::string &) p2.file); if (d < 0) return true; if (d > 0) return false; if (line < p2.line) return true; @@ -68,7 +71,7 @@ struct AttrName typedef std::vector AttrPath; -string showAttrPath(const AttrPath & attrPath); +std::string showAttrPath(const AttrPath & attrPath); /* Abstract syntax of Nix expressions. */ @@ -110,7 +113,7 @@ struct ExprFloat : Expr struct ExprString : Expr { - string s; + std::string s; Value v; ExprString(std::string s) : s(std::move(s)) { v.mkString(this->s.data()); }; COMMON_METHODS @@ -119,9 +122,9 @@ struct ExprString : Expr struct ExprPath : Expr { - string s; + std::string s; Value v; - ExprPath(const string & s) : s(s) { v.mkPath(this->s.c_str()); }; + ExprPath(std::string s) : s(std::move(s)) { v.mkPath(this->s.c_str()); }; COMMON_METHODS Value * maybeThunk(EvalState & state, Env & env); }; @@ -249,7 +252,7 @@ struct ExprLambda : Expr { }; void setName(Symbol & name); - string showNamePos() const; + std::string showNamePos() const; inline bool hasFormals() const { return formals != nullptr; } COMMON_METHODS }; diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index e1d177c1f..919b9cfae 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -244,7 +244,7 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, es2->emplace_back(i->first, e); }; const auto trimString = [&] (const StringToken & t) { - string s2; + std::string s2; for (size_t j = 0; j < t.l; ++j) { if (atStartOfLine) { if (t.p[j] == ' ') { @@ -268,9 +268,9 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, /* Remove the last line if it is empty and consists only of spaces. */ if (n == 1) { - string::size_type p = s2.find_last_of('\n'); - if (p != string::npos && s2.find_first_not_of(' ', p + 1) == string::npos) - s2 = string(s2, 0, p + 1); + std::string::size_type p = s2.find_last_of('\n'); + if (p != std::string::npos && s2.find_first_not_of(' ', p + 1) == std::string::npos) + s2 = std::string(s2, 0, p + 1); } es2->emplace_back(i->first, new ExprString(s2)); @@ -466,7 +466,7 @@ expr_simple $$ = new ExprConcatStrings(CUR_POS, false, $2); } | SPATH { - string path($1.p + 1, $1.l - 2); + std::string path($1.p + 1, $1.l - 2); $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__findFile")), {new ExprVar(data->symbols.create("__nixPath")), @@ -479,7 +479,7 @@ expr_simple .msg = hintfmt("URL literals are disabled"), .errPos = CUR_POS }); - $$ = new ExprString(string($1)); + $$ = new ExprString(std::string($1)); } | '(' expr ')' { $$ = $2; } /* Let expressions `let {..., body = ...}' are just desugared @@ -494,19 +494,19 @@ expr_simple ; string_parts - : STR { $$ = new ExprString(string($1)); } + : STR { $$ = new ExprString(std::string($1)); } | string_parts_interpolated { $$ = new ExprConcatStrings(CUR_POS, true, $1); } | { $$ = new ExprString(""); } ; string_parts_interpolated : string_parts_interpolated STR - { $$ = $1; $1->emplace_back(makeCurPos(@2, data), new ExprString(string($2))); } + { $$ = $1; $1->emplace_back(makeCurPos(@2, data), new ExprString(std::string($2))); } | string_parts_interpolated DOLLAR_CURLY expr '}' { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $3); } | DOLLAR_CURLY expr '}' { $$ = new std::vector >; $$->emplace_back(makeCurPos(@1, data), $2); } | STR DOLLAR_CURLY expr '}' { $$ = new std::vector >; - $$->emplace_back(makeCurPos(@1, data), new ExprString(string($1))); + $$->emplace_back(makeCurPos(@1, data), new ExprString(std::string($1))); $$->emplace_back(makeCurPos(@2, data), $3); } ; @@ -520,7 +520,7 @@ path_start $$ = new ExprPath(path); } | HPATH { - Path path(getHome() + string($1.p + 1, $1.l - 1)); + Path path(getHome() + std::string($1.p + 1, $1.l - 1)); $$ = new ExprPath(path); } ; @@ -738,16 +738,16 @@ Expr * EvalState::parseStdin() } -void EvalState::addToSearchPath(const string & s) +void EvalState::addToSearchPath(const std::string & s) { size_t pos = s.find('='); - string prefix; + std::string prefix; Path path; - if (pos == string::npos) { + if (pos == std::string::npos) { path = s; } else { - prefix = string(s, 0, pos); - path = string(s, pos + 1); + prefix = std::string(s, 0, pos); + path = std::string(s, pos + 1); } searchPath.emplace_back(prefix, path); diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index daf679a5a..aa22c3b61 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -141,7 +141,7 @@ static void mkOutputString( BindingsBuilder & attrs, const StorePath & drvPath, const BasicDerivation & drv, - const std::pair & o) + const std::pair & o) { auto optOutputPath = o.second.path(*state.store, drv.name, o.first); attrs.alloc(o.first).mkString( @@ -314,7 +314,7 @@ void prim_importNative(EvalState & state, const Pos & pos, Value * * args, Value { auto path = realisePath(state, pos, *args[0]); - string sym(state.forceStringNoCtx(*args[1], pos)); + std::string sym(state.forceStringNoCtx(*args[1], pos)); void *handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL); if (!handle) @@ -386,7 +386,7 @@ void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_typeOf(EvalState & state, const Pos & pos, Value * * args, Value & v) { state.forceValue(*args[0], pos); - string t; + std::string t; switch (args[0]->type()) { case nInt: t = "int"; break; case nBool: t = "bool"; break; @@ -707,7 +707,7 @@ static RegisterPrimOp primop_abort({ .fun = [](EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; - string s = state.coerceToString(pos, *args[0], context).toOwned(); + auto s = state.coerceToString(pos, *args[0], context).toOwned(); throw Abort("evaluation aborted with the following error message: '%1%'", s); } }); @@ -725,7 +725,7 @@ static RegisterPrimOp primop_throw({ .fun = [](EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; - string s = state.coerceToString(pos, *args[0], context).toOwned(); + auto s = state.coerceToString(pos, *args[0], context).toOwned(); throw ThrownError(s); } }); @@ -826,7 +826,7 @@ static RegisterPrimOp primop_tryEval({ /* Return an environment variable. Use with care. */ static void prim_getEnv(EvalState & state, const Pos & pos, Value * * args, Value & v) { - string name(state.forceStringNoCtx(*args[0], pos)); + std::string name(state.forceStringNoCtx(*args[0], pos)); v.mkString(evalSettings.restrictEval || evalSettings.pureEval ? "" : getEnv(name).value_or("")); } @@ -935,7 +935,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * pos ); - string drvName; + std::string drvName; Pos & posDrvName(*attr->pos); try { drvName = state.forceStringNoCtx(*attr->value, pos); @@ -973,7 +973,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * for (auto & i : args[0]->attrs->lexicographicOrder()) { if (i->name == state.sIgnoreNulls) continue; - const string & key = i->name; + const std::string & key = i->name; vomit("processing attribute '%1%'", key); auto handleHashMode = [&](const std::string_view s) { @@ -1031,7 +1031,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * else if (i->name == state.sArgs) { state.forceList(*i->value, pos); for (auto elem : i->value->listItems()) { - string s = state.coerceToString(posDrvName, *elem, context, true).toOwned(); + auto s = state.coerceToString(posDrvName, *elem, context, true).toOwned(); drv.args.push_back(s); } } @@ -1118,7 +1118,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * /* Handle derivation outputs of the form ‘!!’. */ else if (path.at(0) == '!') { - std::pair ctx = decodeContext(path); + auto ctx = decodeContext(path); drv.inputDrvs[state.store->parseStorePath(ctx.first)].insert(ctx.second); } @@ -1440,8 +1440,8 @@ static RegisterPrimOp primop_dirOf({ static void prim_readFile(EvalState & state, const Pos & pos, Value * * args, Value & v) { auto path = realisePath(state, pos, *args[0]); - string s = readFile(path); - if (s.find((char) 0) != string::npos) + auto s = readFile(path); + if (s.find((char) 0) != std::string::npos) throw Error("the contents of the file '%1%' cannot be represented as a Nix string", path); StorePathSet refs; if (state.store->isInStore(path)) { @@ -1474,7 +1474,7 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va for (auto v2 : args[0]->listItems()) { state.forceAttrs(*v2, pos); - string prefix; + std::string prefix; Bindings::iterator i = v2->attrs->find(state.sPrefix); if (i != v2->attrs->end()) prefix = state.forceStringNoCtx(*i->value, pos); @@ -1488,7 +1488,7 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va ); PathSet context; - string path = state.coerceToString(pos, *i->value, context, false, false).toOwned(); + auto path = state.coerceToString(pos, *i->value, context, false, false).toOwned(); try { auto rewrites = state.realiseContext(context); @@ -1754,8 +1754,8 @@ static RegisterPrimOp primop_fromJSON({ static void prim_toFile(EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; - string name(state.forceStringNoCtx(*args[0], pos)); - string contents(state.forceString(*args[1], context, pos)); + std::string name(state.forceStringNoCtx(*args[0], pos)); + std::string contents(state.forceString(*args[1], context, pos)); StorePathSet refs; @@ -1863,7 +1863,7 @@ static RegisterPrimOp primop_toFile({ static void addPath( EvalState & state, const Pos & pos, - const string & name, + const std::string & name, Path path, Value * filterFun, FileIngestionMethod method, @@ -2016,14 +2016,14 @@ static void prim_path(EvalState & state, const Pos & pos, Value * * args, Value { state.forceAttrs(*args[0], pos); Path path; - string name; + std::string name; Value * filterFun = nullptr; auto method = FileIngestionMethod::Recursive; std::optional expectedHash; PathSet context; for (auto & attr : *args[0]->attrs) { - const string & n(attr.name); + auto & n(attr.name); if (n == "path") path = state.coerceToPath(*attr.pos, *attr.value, context); else if (attr.name == state.sName) @@ -3616,7 +3616,7 @@ static void prim_concatStringsSep(EvalState & state, const Pos & pos, Value * * auto sep = state.forceString(*args[0], context, pos); state.forceList(*args[1], pos); - string res; + std::string res; res.reserve((args[1]->listSize() + 32) * sep.size()); bool first = true; @@ -3649,12 +3649,12 @@ static void prim_replaceStrings(EvalState & state, const Pos & pos, Value * * ar .errPos = pos }); - std::vector from; + std::vector from; from.reserve(args[0]->listSize()); for (auto elem : args[0]->listItems()) from.emplace_back(state.forceString(*elem, pos)); - std::vector> to; + std::vector> to; to.reserve(args[1]->listSize()); for (auto elem : args[1]->listItems()) { PathSet ctx; @@ -3665,7 +3665,7 @@ static void prim_replaceStrings(EvalState & state, const Pos & pos, Value * * ar PathSet context; auto s = state.forceString(*args[2], context, pos); - string res; + std::string res; // Loops one past last character to handle the case where 'from' contains an empty string. for (size_t p = 0; p <= s.size(); ) { bool found = false; diff --git a/src/libexpr/primops/context.cc b/src/libexpr/primops/context.cc index 654251c23..3701bd442 100644 --- a/src/libexpr/primops/context.cc +++ b/src/libexpr/primops/context.cc @@ -37,7 +37,7 @@ static void prim_unsafeDiscardOutputDependency(EvalState & state, const Pos & po PathSet context2; for (auto & p : context) - context2.insert(p.at(0) == '=' ? string(p, 1) : p); + context2.insert(p.at(0) == '=' ? std::string(p, 1) : p); v.mkString(*s, context2); } @@ -76,13 +76,13 @@ static void prim_getContext(EvalState & state, const Pos & pos, Value * * args, auto contextInfos = std::map(); for (const auto & p : context) { Path drv; - string output; + std::string output; const Path * path = &p; if (p.at(0) == '=') { - drv = string(p, 1); + drv = std::string(p, 1); path = &drv; } else if (p.at(0) == '!') { - std::pair ctx = decodeContext(p); + std::pair ctx = decodeContext(p); drv = ctx.first; output = ctx.second; path = &drv; @@ -166,7 +166,7 @@ static void prim_appendContext(EvalState & state, const Pos & pos, Value * * arg .errPos = *i.pos }); } - context.insert("=" + string(i.name)); + context.insert("=" + std::string(i.name)); } } diff --git a/src/libexpr/primops/fetchMercurial.cc b/src/libexpr/primops/fetchMercurial.cc index c4e1a7bf0..b7f715859 100644 --- a/src/libexpr/primops/fetchMercurial.cc +++ b/src/libexpr/primops/fetchMercurial.cc @@ -62,7 +62,7 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar fetchers::Attrs attrs; attrs.insert_or_assign("type", "hg"); attrs.insert_or_assign("url", url.find("://") != std::string::npos ? url : "file://" + url); - attrs.insert_or_assign("name", string(name)); + attrs.insert_or_assign("name", std::string(name)); if (ref) attrs.insert_or_assign("ref", *ref); if (rev) attrs.insert_or_assign("rev", rev->gitRev()); auto input = fetchers::Input::fromAttrs(std::move(attrs)); diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index 7496104b6..f3e3e70d8 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -186,7 +186,7 @@ static void prim_fetchTree(EvalState & state, const Pos & pos, Value * * args, V static RegisterPrimOp primop_fetchTree("fetchTree", 1, prim_fetchTree); static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v, - const string & who, bool unpack, std::string name) + const std::string & who, bool unpack, std::string name) { std::optional url; std::optional expectedHash; @@ -198,7 +198,7 @@ static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v, state.forceAttrs(*args[0], pos); for (auto & attr : *args[0]->attrs) { - string n(attr.name); + std::string n(attr.name); if (n == "url") url = state.forceStringNoCtx(*attr.value, *attr.pos); else if (n == "sha256") diff --git a/src/libexpr/primops/fromTOML.cc b/src/libexpr/primops/fromTOML.cc index c0e858b61..dd4280030 100644 --- a/src/libexpr/primops/fromTOML.cc +++ b/src/libexpr/primops/fromTOML.cc @@ -9,7 +9,7 @@ static void prim_fromTOML(EvalState & state, const Pos & pos, Value * * args, Va { auto toml = state.forceStringNoCtx(*args[0], pos); - std::istringstream tomlStream(string{toml}); + std::istringstream tomlStream(std::string{toml}); std::function visit; diff --git a/src/libexpr/value-to-xml.cc b/src/libexpr/value-to-xml.cc index a9fb60b0e..afeaf5694 100644 --- a/src/libexpr/value-to-xml.cc +++ b/src/libexpr/value-to-xml.cc @@ -9,7 +9,7 @@ namespace nix { -static XMLAttrs singletonAttrs(const string & name, const string & value) +static XMLAttrs singletonAttrs(const std::string & name, const std::string & value) { XMLAttrs attrs; attrs[name] = value; -- cgit v1.2.3 From 158280d8e9b9d7fbf1ccee9cef829081859810e6 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 27 Feb 2022 15:25:22 +0100 Subject: fetchTree: Do not re-fetch paths already present --- src/libexpr/primops/fetchTree.cc | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index f3e3e70d8..ea416d2d9 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -185,6 +185,13 @@ static void prim_fetchTree(EvalState & state, const Pos & pos, Value * * args, V // FIXME: document static RegisterPrimOp primop_fetchTree("fetchTree", 1, prim_fetchTree); +static void setStorePath(EvalState &state, const StorePath &storePath, Value &v) { + state.allowPath(storePath); + + auto path = state.store->printStorePath(storePath); + v.mkString(path, PathSet({path})); +} + static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v, const std::string & who, bool unpack, std::string name) { @@ -203,6 +210,8 @@ static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v, url = state.forceStringNoCtx(*attr.value, *attr.pos); else if (n == "sha256") expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, *attr.pos), htSHA256); + else if (n == "narHash") + expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, *attr.pos), htSHA256); else if (n == "name") name = state.forceStringNoCtx(*attr.value, *attr.pos); else @@ -230,6 +239,20 @@ static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v, if (evalSettings.pureEval && !expectedHash) throw Error("in pure evaluation mode, '%s' requires a 'sha256' argument", who); + // early exit if pinned and already in the store + if (expectedHash && expectedHash->type == htSHA256) { + auto expectedPath = + unpack + ? state.store->makeFixedOutputPath(FileIngestionMethod::Recursive, *expectedHash, name, {}) + : state.store->makeFixedOutputPath(FileIngestionMethod::Flat, *expectedHash, name, {}); + + auto validPaths = state.store->queryValidPaths({expectedPath}, NoSubstitute); + if (!validPaths.empty()) { + setStorePath(state, expectedPath, v); + return; + } + } + auto storePath = unpack ? fetchers::downloadTarball(state.store, *url, name, (bool) expectedHash).first.storePath @@ -244,10 +267,7 @@ static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v, *url, expectedHash->to_string(Base32, true), hash.to_string(Base32, true)); } - state.allowPath(storePath); - - auto path = state.store->printStorePath(storePath); - v.mkString(path, PathSet({path})); + setStorePath(state, storePath, v); } static void prim_fetchurl(EvalState & state, const Pos & pos, Value * * args, Value & v) -- cgit v1.2.3 From ee019d0afce63f5145a043b025a37dfc39444a3d Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 27 Feb 2022 15:59:34 +0100 Subject: Add EvalState::allowAndSetStorePathString helper This switches addPath from `printStorePath` to `toRealPath`. --- src/libexpr/eval.cc | 8 ++++++++ src/libexpr/eval.hh | 3 +++ src/libexpr/primops.cc | 15 +++++---------- src/libexpr/primops/fetchTree.cc | 11 ++--------- 4 files changed, 18 insertions(+), 19 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 60f0bf08c..d94afbb99 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -517,6 +517,14 @@ void EvalState::allowPath(const StorePath & storePath) allowedPaths->insert(store->toRealPath(storePath)); } +void EvalState::allowAndSetStorePathString(const StorePath &storePath, Value &v) +{ + allowPath(storePath); + + auto path = store->printStorePath(storePath); + v.mkString(path, PathSet({path})); +} + Path EvalState::checkSourcePath(const Path & path_) { if (!allowedPaths) return path_; diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 1f0e97b2e..9324961f7 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -161,6 +161,9 @@ public: the real store path if `store` is a chroot store. */ void allowPath(const StorePath & storePath); + /* Allow access to a store path and return it as a string. */ + void allowAndSetStorePathString(const StorePath & storePath, Value &v); + /* Check whether access to a path is allowed and throw an error if not. Otherwise return the canonicalised path. */ Path checkSourcePath(const Path & path); diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index aa22c3b61..3124025aa 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1919,20 +1919,15 @@ static void addPath( if (expectedHash) expectedStorePath = state.store->makeFixedOutputPath(method, *expectedHash, name); - Path dstPath; if (!expectedHash || !state.store->isValidPath(*expectedStorePath)) { - dstPath = state.store->printStorePath(settings.readOnlyMode + StorePath dstPath = settings.readOnlyMode ? state.store->computeStorePathForPath(name, path, method, htSHA256, filter).first - : state.store->addToStore(name, path, method, htSHA256, filter, state.repair, refs)); - if (expectedHash && expectedStorePath != state.store->parseStorePath(dstPath)) + : state.store->addToStore(name, path, method, htSHA256, filter, state.repair, refs); + if (expectedHash && expectedStorePath != dstPath) throw Error("store path mismatch in (possibly filtered) path added from '%s'", path); + state.allowAndSetStorePathString(dstPath, v); } else - dstPath = state.store->printStorePath(*expectedStorePath); - - v.mkString(dstPath, {dstPath}); - - state.allowPath(dstPath); - + state.allowAndSetStorePathString(*expectedStorePath, v); } catch (Error & e) { e.addTrace(pos, "while adding path '%s'", path); throw; diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index ea416d2d9..2eeee7173 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -185,13 +185,6 @@ static void prim_fetchTree(EvalState & state, const Pos & pos, Value * * args, V // FIXME: document static RegisterPrimOp primop_fetchTree("fetchTree", 1, prim_fetchTree); -static void setStorePath(EvalState &state, const StorePath &storePath, Value &v) { - state.allowPath(storePath); - - auto path = state.store->printStorePath(storePath); - v.mkString(path, PathSet({path})); -} - static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v, const std::string & who, bool unpack, std::string name) { @@ -248,7 +241,7 @@ static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v, auto validPaths = state.store->queryValidPaths({expectedPath}, NoSubstitute); if (!validPaths.empty()) { - setStorePath(state, expectedPath, v); + state.allowAndSetStorePathString(expectedPath, v); return; } } @@ -267,7 +260,7 @@ static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v, *url, expectedHash->to_string(Base32, true), hash.to_string(Base32, true)); } - setStorePath(state, storePath, v); + state.allowAndSetStorePathString(storePath, v); } static void prim_fetchurl(EvalState & state, const Pos & pos, Value * * args, Value & v) -- cgit v1.2.3 From ea71da395fe23fc0b4adb6fe5075742441e8baf8 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 1 Mar 2022 01:29:34 +0000 Subject: Move some stuff from `Settings` to a new `FetchSettings`. Starting work on #5638 The exact boundary between `FetchSettings` and `EvalSettings` is not clear to me, but that's fine. First lets clean out `libstore`, and then worry about what, if anything, should be the separation between those two. --- src/libexpr/flake/config.cc | 3 ++- src/libexpr/flake/flake.cc | 9 +++++---- 2 files changed, 7 insertions(+), 5 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/flake/config.cc b/src/libexpr/flake/config.cc index 7ecd61816..a811e59a1 100644 --- a/src/libexpr/flake/config.cc +++ b/src/libexpr/flake/config.cc @@ -1,5 +1,6 @@ #include "flake.hh" #include "globals.hh" +#include "fetch-settings.hh" #include @@ -53,7 +54,7 @@ void ConfigFile::apply() auto trustedList = readTrustedList(); bool trusted = false; - if (nix::settings.acceptFlakeConfig){ + if (nix::fetchSettings.acceptFlakeConfig){ trusted = true; } else if (auto saved = get(get(trustedList, name).value_or(std::map()), valueS)) { trusted = *saved; diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc index ad0881641..6a1aca40d 100644 --- a/src/libexpr/flake/flake.cc +++ b/src/libexpr/flake/flake.cc @@ -6,6 +6,7 @@ #include "store-api.hh" #include "fetchers.hh" #include "finally.hh" +#include "fetch-settings.hh" namespace nix { @@ -315,7 +316,7 @@ LockedFlake lockFlake( FlakeCache flakeCache; - auto useRegistries = lockFlags.useRegistries.value_or(settings.useRegistries); + auto useRegistries = lockFlags.useRegistries.value_or(fetchSettings.useRegistries); auto flake = getFlake(state, topRef, useRegistries, flakeCache); @@ -591,7 +592,7 @@ LockedFlake lockFlake( if (lockFlags.writeLockFile) { if (auto sourcePath = topRef.input.getSourcePath()) { if (!newLockFile.isImmutable()) { - if (settings.warnDirty) + if (fetchSettings.warnDirty) warn("will not write lock file of flake '%s' because it has a mutable input", topRef); } else { if (!lockFlags.updateLockFile) @@ -618,7 +619,7 @@ LockedFlake lockFlake( if (lockFlags.commitLockFile) { std::string cm; - cm = settings.commitLockFileSummary.get(); + cm = fetchSettings.commitLockFileSummary.get(); if (cm == "") { cm = fmt("%s: %s", relPath, lockFileExists ? "Update" : "Add"); @@ -716,7 +717,7 @@ static void prim_getFlake(EvalState & state, const Pos & pos, Value * * args, Va lockFlake(state, flakeRef, LockFlags { .updateLockFile = false, - .useRegistries = !evalSettings.pureEval && settings.useRegistries, + .useRegistries = !evalSettings.pureEval && fetchSettings.useRegistries, .allowMutable = !evalSettings.pureEval, }), v); -- cgit v1.2.3 From d974d2ad59d1cc8aee63e2a18124e58c1084ff65 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 1 Mar 2022 11:29:19 +0100 Subject: fetch{url,Tarball}: Remove 'narHash' attribute This was introduced in #6174. However fetch{url,Tarball} are legacy and we shouldn't have an undocumented attribute that does the same thing as one that already exists ('sha256'). --- src/libexpr/eval.cc | 2 +- src/libexpr/eval.hh | 2 +- src/libexpr/primops/fetchTree.cc | 2 -- 3 files changed, 2 insertions(+), 4 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index d94afbb99..8c5888497 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -517,7 +517,7 @@ void EvalState::allowPath(const StorePath & storePath) allowedPaths->insert(store->toRealPath(storePath)); } -void EvalState::allowAndSetStorePathString(const StorePath &storePath, Value &v) +void EvalState::allowAndSetStorePathString(const StorePath &storePath, Value & v) { allowPath(storePath); diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 9324961f7..36a53729a 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -162,7 +162,7 @@ public: void allowPath(const StorePath & storePath); /* Allow access to a store path and return it as a string. */ - void allowAndSetStorePathString(const StorePath & storePath, Value &v); + void allowAndSetStorePathString(const StorePath & storePath, Value & v); /* Check whether access to a path is allowed and throw an error if not. Otherwise return the canonicalised path. */ diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index 2eeee7173..cb929b99a 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -203,8 +203,6 @@ static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v, url = state.forceStringNoCtx(*attr.value, *attr.pos); else if (n == "sha256") expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, *attr.pos), htSHA256); - else if (n == "narHash") - expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, *attr.pos), htSHA256); else if (n == "name") name = state.forceStringNoCtx(*attr.value, *attr.pos); else -- cgit v1.2.3 From b6deca7c0dcf6e5f0da1d186dc1a1288325e1d88 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 1 Mar 2022 12:11:10 +0100 Subject: fetchTree: Use isValidPath, add comment --- src/libexpr/primops/fetchTree.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index 2eeee7173..892c59fba 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -239,13 +239,14 @@ static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v, ? state.store->makeFixedOutputPath(FileIngestionMethod::Recursive, *expectedHash, name, {}) : state.store->makeFixedOutputPath(FileIngestionMethod::Flat, *expectedHash, name, {}); - auto validPaths = state.store->queryValidPaths({expectedPath}, NoSubstitute); - if (!validPaths.empty()) { + if (state.store->isValidPath(expectedPath)) { state.allowAndSetStorePathString(expectedPath, v); return; } } + // TODO: fetching may fail, yet the path may be substitutable. + // https://github.com/NixOS/nix/issues/4313 auto storePath = unpack ? fetchers::downloadTarball(state.store, *url, name, (bool) expectedHash).first.storePath -- cgit v1.2.3 From b55d79728ccbd2581fa3aa7b2ec7f498aa2285d6 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 2 Mar 2022 10:57:19 +0100 Subject: Add EvalState::coerceToStorePath() helper This is useful whenever we want to evaluate something to a store path (e.g. in get-drvs.cc). Extracted from the lazy-trees branch (where we can require that a store path must come from a store source tree accessor). --- src/libexpr/eval.cc | 12 ++++++++++++ src/libexpr/eval.hh | 3 +++ src/libexpr/get-drvs.cc | 33 +++++++++++++++++++++------------ src/libexpr/get-drvs.hh | 16 +++++++++------- 4 files changed, 45 insertions(+), 19 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 8c5888497..193358161 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -2058,6 +2058,18 @@ Path EvalState::coerceToPath(const Pos & pos, Value & v, PathSet & context) } +StorePath EvalState::coerceToStorePath(const Pos & pos, Value & v, PathSet & context) +{ + auto path = coerceToString(pos, v, context, false, false).toOwned(); + if (auto storePath = store->maybeParseStorePath(path)) + return *storePath; + throw EvalError({ + .msg = hintfmt("path '%1%' is not in the Nix store", path), + .errPos = pos + }); +} + + bool EvalState::eqValues(Value & v1, Value & v2) { forceValue(v1, noPos); diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 36a53729a..800b00eef 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -272,6 +272,9 @@ public: path. Nothing is copied to the store. */ Path coerceToPath(const Pos & pos, Value & v, PathSet & context); + /* Like coerceToPath, but the result must be a store path. */ + StorePath coerceToStorePath(const Pos & pos, Value & v, PathSet & context); + public: /* The base environment, containing the builtin functions and diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc index 8393e4225..7630c5ff4 100644 --- a/src/libexpr/get-drvs.cc +++ b/src/libexpr/get-drvs.cc @@ -22,7 +22,7 @@ DrvInfo::DrvInfo(EvalState & state, ref store, const std::string & drvPat { auto [drvPath, selectedOutputs] = parsePathWithOutputs(*store, drvPathWithOutputs); - this->drvPath = store->printStorePath(drvPath); + this->drvPath = drvPath; auto drv = store->derivationFromPath(drvPath); @@ -41,9 +41,7 @@ DrvInfo::DrvInfo(EvalState & state, ref store, const std::string & drvPat throw Error("derivation '%s' does not have output '%s'", store->printStorePath(drvPath), outputName); auto & [outputName, output] = *i; - auto optStorePath = output.path(*store, drv.name, outputName); - if (optStorePath) - outPath = store->printStorePath(*optStorePath); + outPath = {output.path(*store, drv.name, outputName)}; } @@ -68,24 +66,35 @@ std::string DrvInfo::querySystem() const } -std::string DrvInfo::queryDrvPath() const +std::optional DrvInfo::queryDrvPath() const { - if (drvPath == "" && attrs) { + if (!drvPath && attrs) { Bindings::iterator i = attrs->find(state->sDrvPath); PathSet context; - drvPath = i != attrs->end() ? state->coerceToPath(*i->pos, *i->value, context) : ""; + if (i == attrs->end()) + drvPath = {std::nullopt}; + else + drvPath = {state->coerceToStorePath(*i->pos, *i->value, context)}; } - return drvPath; + return drvPath.value_or(std::nullopt); } -std::string DrvInfo::queryOutPath() const +StorePath DrvInfo::requireDrvPath() const +{ + if (auto drvPath = queryDrvPath()) + return *drvPath; + throw Error("derivation does not contain a 'drvPath' attribute"); +} + + +StorePath DrvInfo::queryOutPath() const { if (!outPath && attrs) { Bindings::iterator i = attrs->find(state->sOutPath); PathSet context; if (i != attrs->end()) - outPath = state->coerceToPath(*i->pos, *i->value, context); + outPath = state->coerceToStorePath(*i->pos, *i->value, context); } if (!outPath) throw UnimplementedError("CA derivations are not yet supported"); @@ -113,10 +122,10 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool onlyOutputsToInstall) Bindings::iterator outPath = out->value->attrs->find(state->sOutPath); if (outPath == out->value->attrs->end()) continue; // FIXME: throw error? PathSet context; - outputs[name] = state->coerceToPath(*outPath->pos, *outPath->value, context); + outputs.emplace(name, state->coerceToStorePath(*outPath->pos, *outPath->value, context)); } } else - outputs["out"] = queryOutPath(); + outputs.emplace("out", queryOutPath()); } if (!onlyOutputsToInstall || !attrs) return outputs; diff --git a/src/libexpr/get-drvs.hh b/src/libexpr/get-drvs.hh index d13847785..3ca6f1fca 100644 --- a/src/libexpr/get-drvs.hh +++ b/src/libexpr/get-drvs.hh @@ -1,6 +1,7 @@ #pragma once #include "eval.hh" +#include "path.hh" #include #include @@ -12,15 +13,15 @@ namespace nix { struct DrvInfo { public: - typedef std::map Outputs; + typedef std::map Outputs; private: EvalState * state; mutable std::string name; mutable std::string system; - mutable std::string drvPath; - mutable std::optional outPath; + mutable std::optional> drvPath; + mutable std::optional outPath; mutable std::string outputName; Outputs outputs; @@ -41,8 +42,9 @@ public: std::string queryName() const; std::string querySystem() const; - std::string queryDrvPath() const; - std::string queryOutPath() const; + std::optional queryDrvPath() const; + StorePath requireDrvPath() const; + StorePath queryOutPath() const; std::string queryOutputName() const; /** Return the list of outputs. The "outputs to install" are determined by `meta.outputsToInstall`. */ Outputs queryOutputs(bool onlyOutputsToInstall = false); @@ -61,8 +63,8 @@ public: */ void setName(const std::string & s) { name = s; } - void setDrvPath(const std::string & s) { drvPath = s; } - void setOutPath(const std::string & s) { outPath = s; } + void setDrvPath(StorePath path) { drvPath = {{std::move(path)}}; } + void setOutPath(StorePath path) { outPath = {{std::move(path)}}; } void setFailed() { failed = true; }; bool hasFailed() { return failed; }; -- cgit v1.2.3 From 609779086301a600f4282a629d586bba6b6a485c Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 3 Mar 2022 11:11:16 +0100 Subject: Fix segfault in headerCallback() https://hydra.nixos.org/build/168594664 --- src/libexpr/flake/flakeref.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/flake/flakeref.cc b/src/libexpr/flake/flakeref.cc index 930ed9ccd..c1eae413f 100644 --- a/src/libexpr/flake/flakeref.cc +++ b/src/libexpr/flake/flakeref.cc @@ -98,7 +98,7 @@ std::pair parseFlakeRefWithFragment( if (std::regex_match(url, match, flakeRegex)) { auto parsedURL = ParsedURL{ .url = url, - .base = "flake:" + std::string(match[1]), + .base = "flake:" + match.str(1), .scheme = "flake", .authority = "", .path = match[1], @@ -106,12 +106,12 @@ std::pair parseFlakeRefWithFragment( return std::make_pair( FlakeRef(Input::fromURL(parsedURL), ""), - percentDecode(std::string(match[6]))); + percentDecode(match.str(6))); } else if (std::regex_match(url, match, pathUrlRegex)) { std::string path = match[1]; - std::string fragment = percentDecode(std::string(match[3])); + std::string fragment = percentDecode(match.str(3)); if (baseDir) { /* Check if 'url' is a path (either absolute or relative -- cgit v1.2.3 From ecff9d969acef5a08d3963d16eac83a5210de86e Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 3 Mar 2022 13:07:50 +0100 Subject: printValue(): Don't show repeated values Fixes #6157. --- src/libexpr/eval.cc | 25 +++++++++++++------------ src/libexpr/value.hh | 4 ++-- 2 files changed, 15 insertions(+), 14 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 193358161..d9d36fab2 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -86,15 +86,10 @@ RootValue allocRootValue(Value * v) } -void printValue(std::ostream & str, std::set & active, const Value & v) +void printValue(std::ostream & str, std::set & seen, const Value & v) { checkInterrupt(); - if (!active.insert(&v).second) { - str << ""; - return; - } - switch (v.internalType) { case tInt: str << v.integer; @@ -120,10 +115,14 @@ void printValue(std::ostream & str, std::set & active, const Valu str << "null"; break; case tAttrs: { + seen.insert(&v); str << "{ "; for (auto & i : v.attrs->lexicographicOrder()) { str << i->name << " = "; - printValue(str, active, *i->value); + if (seen.count(i->value)) + str << ""; + else + printValue(str, seen, *i->value); str << "; "; } str << "}"; @@ -132,9 +131,13 @@ void printValue(std::ostream & str, std::set & active, const Valu case tList1: case tList2: case tListN: + seen.insert(&v); str << "[ "; for (auto v2 : v.listItems()) { - printValue(str, active, *v2); + if (seen.count(v2)) + str << ""; + else + printValue(str, seen, *v2); str << " "; } str << "]"; @@ -161,15 +164,13 @@ void printValue(std::ostream & str, std::set & active, const Valu default: abort(); } - - active.erase(&v); } std::ostream & operator << (std::ostream & str, const Value & v) { - std::set active; - printValue(str, active, v); + std::set seen; + printValue(str, seen, v); return str; } diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh index 3fdff71a5..84c4d09f8 100644 --- a/src/libexpr/value.hh +++ b/src/libexpr/value.hh @@ -114,8 +114,8 @@ struct Value private: InternalType internalType; -friend std::string showType(const Value & v); -friend void printValue(std::ostream & str, std::set & active, const Value & v); + friend std::string showType(const Value & v); + friend void printValue(std::ostream & str, std::set & seen, const Value & v); public: -- cgit v1.2.3 From e9c04c3351b4b3acae2d154b9aba808c3600054f Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 3 Mar 2022 13:29:14 +0100 Subject: Be more aggressive in hiding repeated values We now memoize on Bindings / list element vectors rather than Values, so that e.g. two Values that point to the same Bindings will be printed only once. --- src/libexpr/eval.cc | 38 +++++++++++++++++++------------------- src/libexpr/value.hh | 2 +- 2 files changed, 20 insertions(+), 20 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index d9d36fab2..2d7309738 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -86,7 +86,7 @@ RootValue allocRootValue(Value * v) } -void printValue(std::ostream & str, std::set & seen, const Value & v) +void printValue(std::ostream & str, std::set & seen, const Value & v) { checkInterrupt(); @@ -115,32 +115,32 @@ void printValue(std::ostream & str, std::set & seen, const Value str << "null"; break; case tAttrs: { - seen.insert(&v); - str << "{ "; - for (auto & i : v.attrs->lexicographicOrder()) { - str << i->name << " = "; - if (seen.count(i->value)) - str << ""; - else + if (!v.attrs->empty() && !seen.insert(v.attrs).second) + str << ""; + else { + str << "{ "; + for (auto & i : v.attrs->lexicographicOrder()) { + str << i->name << " = "; printValue(str, seen, *i->value); - str << "; "; + str << "; "; + } + str << "}"; } - str << "}"; break; } case tList1: case tList2: case tListN: - seen.insert(&v); - str << "[ "; - for (auto v2 : v.listItems()) { - if (seen.count(v2)) - str << ""; - else + if (v.listSize() && !seen.insert(v.listElems()).second) + str << ""; + else { + str << "[ "; + for (auto v2 : v.listItems()) { printValue(str, seen, *v2); - str << " "; + str << " "; + } + str << "]"; } - str << "]"; break; case tThunk: case tApp: @@ -169,7 +169,7 @@ void printValue(std::ostream & str, std::set & seen, const Value std::ostream & operator << (std::ostream & str, const Value & v) { - std::set seen; + std::set seen; printValue(str, seen, v); return str; } diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh index 84c4d09f8..d0fa93e92 100644 --- a/src/libexpr/value.hh +++ b/src/libexpr/value.hh @@ -115,7 +115,7 @@ private: InternalType internalType; friend std::string showType(const Value & v); - friend void printValue(std::ostream & str, std::set & seen, const Value & v); + friend void printValue(std::ostream & str, std::set & seen, const Value & v); public: -- cgit v1.2.3 From c0792b1546ceed1c02a02ca1286ead55f79d5798 Mon Sep 17 00:00:00 2001 From: regnat Date: Thu, 3 Mar 2022 10:50:35 +0100 Subject: Implement a suggestions mechanism Each `Error` class now includes a set of suggestions, and these are printed by the top-level handler. --- src/libexpr/attr-path.cc | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/attr-path.cc b/src/libexpr/attr-path.cc index eb0e706c7..32deecfae 100644 --- a/src/libexpr/attr-path.cc +++ b/src/libexpr/attr-path.cc @@ -74,8 +74,14 @@ std::pair findAlongAttrPath(EvalState & state, const std::string & throw Error("empty attribute name in selection path '%1%'", attrPath); Bindings::iterator a = v->attrs->find(state.symbols.create(attr)); - if (a == v->attrs->end()) - throw AttrPathNotFound("attribute '%1%' in selection path '%2%' not found", attr, attrPath); + if (a == v->attrs->end()) { + std::set attrNames; + for (auto & attr : *v->attrs) + attrNames.insert(attr.name); + + auto suggestions = Suggestions::bestMatches(attrNames, attr); + throw AttrPathNotFound(suggestions, "attribute '%1%' in selection path '%2%' not found", attr, attrPath); + } v = &*a->value; pos = *a->pos; } -- cgit v1.2.3 From 98e361ad4c1a26d4ffe4762a6f33bb9e39321a39 Mon Sep 17 00:00:00 2001 From: regnat Date: Fri, 4 Mar 2022 09:44:00 +0100 Subject: Also display suggestions for the commands using the eval cache Make `nix build .#nix-armv8l-linux` work for example --- src/libexpr/eval-cache.cc | 35 +++++++++++++++++++++++++++-------- src/libexpr/eval-cache.hh | 8 +++++--- 2 files changed, 32 insertions(+), 11 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/eval-cache.cc b/src/libexpr/eval-cache.cc index 00d0749f9..188223957 100644 --- a/src/libexpr/eval-cache.cc +++ b/src/libexpr/eval-cache.cc @@ -406,6 +406,16 @@ Value & AttrCursor::forceValue() return v; } +Suggestions AttrCursor::getSuggestionsForAttr(Symbol name) +{ + auto attrNames = getAttrs(); + std::set strAttrNames; + for (auto & name : attrNames) + strAttrNames.insert(std::string(name)); + + return Suggestions::bestMatches(strAttrNames, name); +} + std::shared_ptr AttrCursor::maybeGetAttr(Symbol name, bool forceErrors) { if (root->db) { @@ -446,6 +456,11 @@ std::shared_ptr AttrCursor::maybeGetAttr(Symbol name, bool forceErro return nullptr; //throw TypeError("'%s' is not an attribute set", getAttrPathStr()); + for (auto & attr : *v.attrs) { + if (root->db) + root->db->setPlaceholder({cachedValue->first, attr.name}); + } + auto attr = v.attrs->get(name); if (!attr) { @@ -464,7 +479,7 @@ std::shared_ptr AttrCursor::maybeGetAttr(Symbol name, bool forceErro cachedValue2 = {root->db->setPlaceholder({cachedValue->first, name}), placeholder_t()}; } - return std::make_shared( + return make_ref( root, std::make_pair(shared_from_this(), name), attr->value, std::move(cachedValue2)); } @@ -473,27 +488,31 @@ std::shared_ptr AttrCursor::maybeGetAttr(std::string_view name) return maybeGetAttr(root->state.symbols.create(name)); } -std::shared_ptr AttrCursor::getAttr(Symbol name, bool forceErrors) +ref AttrCursor::getAttr(Symbol name, bool forceErrors) { auto p = maybeGetAttr(name, forceErrors); if (!p) throw Error("attribute '%s' does not exist", getAttrPathStr(name)); - return p; + return ref(p); } -std::shared_ptr AttrCursor::getAttr(std::string_view name) +ref AttrCursor::getAttr(std::string_view name) { return getAttr(root->state.symbols.create(name)); } -std::shared_ptr AttrCursor::findAlongAttrPath(const std::vector & attrPath, bool force) +OrSuggestions> AttrCursor::findAlongAttrPath(const std::vector & attrPath, bool force) { auto res = shared_from_this(); for (auto & attr : attrPath) { - res = res->maybeGetAttr(attr, force); - if (!res) return {}; + auto child = res->maybeGetAttr(attr, force); + if (!child) { + auto suggestions = res->getSuggestionsForAttr(attr); + return OrSuggestions>::failed(suggestions); + } + res = child; } - return res; + return ref(res); } std::string AttrCursor::getString() diff --git a/src/libexpr/eval-cache.hh b/src/libexpr/eval-cache.hh index 43b34ebcb..40f1d4ffc 100644 --- a/src/libexpr/eval-cache.hh +++ b/src/libexpr/eval-cache.hh @@ -94,15 +94,17 @@ public: std::string getAttrPathStr(Symbol name) const; + Suggestions getSuggestionsForAttr(Symbol name); + std::shared_ptr maybeGetAttr(Symbol name, bool forceErrors = false); std::shared_ptr maybeGetAttr(std::string_view name); - std::shared_ptr getAttr(Symbol name, bool forceErrors = false); + ref getAttr(Symbol name, bool forceErrors = false); - std::shared_ptr getAttr(std::string_view name); + ref getAttr(std::string_view name); - std::shared_ptr findAlongAttrPath(const std::vector & attrPath, bool force = false); + OrSuggestions> findAlongAttrPath(const std::vector & attrPath, bool force = false); std::string getString(); -- cgit v1.2.3 From fd45d85b4149a4d153d9c60f7f77244b3cf29ae3 Mon Sep 17 00:00:00 2001 From: regnat Date: Fri, 4 Mar 2022 13:42:43 +0100 Subject: Move OrSuggestions to its own header Prevents a recursive inclusion --- src/libexpr/eval-cache.hh | 1 + 1 file changed, 1 insertion(+) (limited to 'src/libexpr') diff --git a/src/libexpr/eval-cache.hh b/src/libexpr/eval-cache.hh index 40f1d4ffc..c0ca76bcf 100644 --- a/src/libexpr/eval-cache.hh +++ b/src/libexpr/eval-cache.hh @@ -3,6 +3,7 @@ #include "sync.hh" #include "hash.hh" #include "eval.hh" +#include "or-suggestions.hh" #include #include -- cgit v1.2.3 From 1b978596b575bda096a9e896a5cc3502943e5a60 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 26 Feb 2022 02:30:11 +0100 Subject: Value::mkString: Avoid crash from null string_view --- src/libexpr/eval.cc | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'src/libexpr') diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 2d7309738..b954e7bd9 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -805,7 +805,13 @@ LocalNoInline(void addErrorTrace(Error & e, const Pos & pos, const char * s, con void Value::mkString(std::string_view s) { - mkString(dupStringWithLen(s.data(), s.size())); + if (s.size() == 0) { + // s.data() may not be valid and we don't need to allocate. + mkString(""); + } + else { + mkString(dupStringWithLen(s.data(), s.size())); + } } -- cgit v1.2.3 From bbf55383e7a9914cf79b8879e234f444cc774bfa Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 26 Feb 2022 02:30:29 +0100 Subject: Value::mkPath: Avoid potential crash from null string_view --- src/libexpr/eval.cc | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'src/libexpr') diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index b954e7bd9..7f82aa5f4 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -842,7 +842,14 @@ void Value::mkStringMove(const char * s, const PathSet & context) void Value::mkPath(std::string_view s) { - mkPath(dupStringWithLen(s.data(), s.size())); + if (s.size() == 0) { + // Pathological, but better than crashing in dupStringWithLen, as + // s.data() may be null. + mkPath(""); + } + else { + mkPath(dupStringWithLen(s.data(), s.size())); + } } -- cgit v1.2.3 From da260f579d5f4a66d6d0d0e1297b69666d049d03 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 27 Feb 2022 12:50:18 +0100 Subject: dupStringWithLen -> makeImmutableString Refactor the `size == 0` logic into a new helper function that replaces dupStringWithLen. The name had to change, because unlike a `dup`-function, it does not always allocate a new string. --- src/libexpr/eval.cc | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) (limited to 'src/libexpr') diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 7f82aa5f4..5bf161cc0 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -63,9 +63,15 @@ static char * dupString(const char * s) } -static char * dupStringWithLen(const char * s, size_t size) +// When there's no need to write to the string, we can optimize away empty +// string allocations. +// This function handles makeImmutableStringWithLen(null, 0) by returning the +// empty string. +static const char * makeImmutableStringWithLen(const char * s, size_t size) { char * t; + if (size == 0) + return ""; #if HAVE_BOEHMGC t = GC_STRNDUP(s, size); #else @@ -75,6 +81,10 @@ static char * dupStringWithLen(const char * s, size_t size) return t; } +static inline const char * makeImmutableString(std::string_view s) { + return makeImmutableStringWithLen(s.data(), s.size()); +} + RootValue allocRootValue(Value * v) { @@ -805,13 +815,7 @@ LocalNoInline(void addErrorTrace(Error & e, const Pos & pos, const char * s, con void Value::mkString(std::string_view s) { - if (s.size() == 0) { - // s.data() may not be valid and we don't need to allocate. - mkString(""); - } - else { - mkString(dupStringWithLen(s.data(), s.size())); - } + mkString(makeImmutableString(s)); } @@ -842,14 +846,7 @@ void Value::mkStringMove(const char * s, const PathSet & context) void Value::mkPath(std::string_view s) { - if (s.size() == 0) { - // Pathological, but better than crashing in dupStringWithLen, as - // s.data() may be null. - mkPath(""); - } - else { - mkPath(dupStringWithLen(s.data(), s.size())); - } + mkPath(makeImmutableString(s)); } -- cgit v1.2.3 From dcf4f77fac8bc214afd4851b367ce386f0a34658 Mon Sep 17 00:00:00 2001 From: regnat Date: Mon, 7 Mar 2022 17:45:35 +0100 Subject: Merge `or-suggestions.hh` into `suggestions.hh` No real need for keeping a separate header for such a simple class. This requires changing a bit `OrSuggestions::operator*` to not throw an `Error` to prevent a cyclic dependency. But since this error is only thrown on programmer error, we can replace the whole method by a direct call to `std::get` which will raise its own assertion if needs be. --- src/libexpr/eval-cache.hh | 1 - 1 file changed, 1 deletion(-) (limited to 'src/libexpr') diff --git a/src/libexpr/eval-cache.hh b/src/libexpr/eval-cache.hh index c0ca76bcf..40f1d4ffc 100644 --- a/src/libexpr/eval-cache.hh +++ b/src/libexpr/eval-cache.hh @@ -3,7 +3,6 @@ #include "sync.hh" #include "hash.hh" #include "eval.hh" -#include "or-suggestions.hh" #include #include -- cgit v1.2.3