From 7c716b4c49a9d81246ce06938a7b53553b961676 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 22 Apr 2019 23:25:47 +0200 Subject: [PATCH] Checkpoint --- src/libexpr/attr-path.cc | 8 +- src/libexpr/attr-path.hh | 2 +- src/libexpr/attr-set.cc | 13 +- src/libexpr/attr-set.hh | 7 +- src/libexpr/common-eval-args.cc | 4 +- src/libexpr/common-eval-args.hh | 5 +- src/libexpr/eval-inline.hh | 12 +- src/libexpr/eval.cc | 209 +++++++++--------- src/libexpr/eval.hh | 11 +- src/libexpr/gc.cc | 235 +++++++++++--------- src/libexpr/gc.hh | 263 +++++++++++++++-------- src/libexpr/get-drvs.cc | 29 +-- src/libexpr/get-drvs.hh | 3 +- src/libexpr/nixexpr.hh | 12 +- src/libexpr/primops.cc | 75 ++++--- src/libexpr/value.hh | 12 +- src/nix-build/nix-build.cc | 10 +- src/nix-env/nix-env.cc | 19 +- src/nix-env/user-env.cc | 15 +- src/nix-instantiate/nix-instantiate.cc | 16 +- src/nix-prefetch-url/nix-prefetch-url.cc | 24 +-- src/nix/command.hh | 7 +- src/nix/edit.cc | 2 +- src/nix/installables.cc | 14 +- src/nix/repl.cc | 22 +- src/nix/search.cc | 4 +- tests/common.sh.in | 3 + 27 files changed, 596 insertions(+), 440 deletions(-) diff --git a/src/libexpr/attr-path.cc b/src/libexpr/attr-path.cc index b0f80db32..fde7b76b9 100644 --- a/src/libexpr/attr-path.cc +++ b/src/libexpr/attr-path.cc @@ -32,7 +32,7 @@ static Strings parseAttrPath(const string & s) } -Value * findAlongAttrPath(EvalState & state, const string & attrPath, +Ptr findAlongAttrPath(EvalState & state, const string & attrPath, Bindings & autoArgs, Value & vIn) { Strings tokens = parseAttrPath(attrPath); @@ -40,7 +40,7 @@ Value * findAlongAttrPath(EvalState & state, const string & attrPath, Error attrError = Error(format("attribute selection path '%1%' does not match expression") % attrPath); - Value * v = &vIn; + Ptr v(&vIn); for (auto & attr : tokens) { @@ -50,7 +50,7 @@ Value * findAlongAttrPath(EvalState & state, const string & attrPath, if (string2Int(attr, attrIndex)) apType = apIndex; /* Evaluate the expression. */ - Value * vNew = state.allocValue(); + auto vNew = state.allocValue(); state.autoCallFunction(autoArgs, *v, *vNew); v = vNew; state.forceValue(*v); @@ -71,7 +71,7 @@ Value * findAlongAttrPath(EvalState & state, const string & attrPath, Bindings::iterator a = v->attrs->find(state.symbols.create(attr)); if (a == v->attrs->end()) throw Error(format("attribute '%1%' in selection path '%2%' not found") % attr % attrPath); - v = &*a->value; + v = a->value; } else if (apType == apIndex) { diff --git a/src/libexpr/attr-path.hh b/src/libexpr/attr-path.hh index 46a341950..c82b3672d 100644 --- a/src/libexpr/attr-path.hh +++ b/src/libexpr/attr-path.hh @@ -7,7 +7,7 @@ namespace nix { -Value * findAlongAttrPath(EvalState & state, const string & attrPath, +Ptr findAlongAttrPath(EvalState & state, const string & attrPath, Bindings & autoArgs, Value & vIn); } diff --git a/src/libexpr/attr-set.cc b/src/libexpr/attr-set.cc index d1aef97a4..6e958581b 100644 --- a/src/libexpr/attr-set.cc +++ b/src/libexpr/attr-set.cc @@ -20,14 +20,15 @@ Ptr Bindings::allocBindings(size_t capacity) void EvalState::mkAttrs(Value & v, size_t capacity) { - v.type = tAttrs; if (capacity == 0) { v.attrs = emptyBindings; - return; + v.type = tAttrs; + } else { + v.attrs = Bindings::allocBindings(capacity); + v.type = tAttrs; + nrAttrsets++; + nrAttrsInAttrsets += capacity; } - v.attrs = Bindings::allocBindings(capacity); - nrAttrsets++; - nrAttrsInAttrsets += capacity; } @@ -36,7 +37,7 @@ void EvalState::mkAttrs(Value & v, size_t capacity) this attribute. */ Value * EvalState::allocAttr(Value & vAttrs, const Symbol & name) { - Value * v = allocValue(); + auto v = allocValue(); vAttrs.attrs->push_back(Attr(name, v)); return v; } diff --git a/src/libexpr/attr-set.hh b/src/libexpr/attr-set.hh index 02241ce74..cb29ee74e 100644 --- a/src/libexpr/attr-set.hh +++ b/src/libexpr/attr-set.hh @@ -42,11 +42,11 @@ private: size_t size_; Attr attrs[0]; -public: - // FIXME: make private - Bindings(size_t capacity) : Object(tBindings, capacity), size_(0) { } + Bindings(size_t capacity) : Object(tBindings, capacity), size_(0) {} Bindings(const Bindings & bindings) = delete; +public: + size_t size() const { return size_; } bool empty() const { return !size_; } @@ -56,6 +56,7 @@ public: void push_back(const Attr & attr) { assert(size_ < capacity()); + gc.assertObject(attr.value); attrs[size_++] = attr; } diff --git a/src/libexpr/common-eval-args.cc b/src/libexpr/common-eval-args.cc index 922b44fcf..d042f8349 100644 --- a/src/libexpr/common-eval-args.cc +++ b/src/libexpr/common-eval-args.cc @@ -28,9 +28,9 @@ MixEvalArgs::MixEvalArgs() .handler([&](std::string s) { searchPath.push_back(s); }); } -Bindings * MixEvalArgs::getAutoArgs(EvalState & state) +Ptr MixEvalArgs::getAutoArgs(EvalState & state) { - Bindings * res = Bindings::allocBindings(autoArgs.size()); + auto res = Bindings::allocBindings(autoArgs.size()); for (auto & i : autoArgs) { Value * v = state.allocValue(); if (i.second[0] == 'E') diff --git a/src/libexpr/common-eval-args.hh b/src/libexpr/common-eval-args.hh index be7fda783..f541aafec 100644 --- a/src/libexpr/common-eval-args.hh +++ b/src/libexpr/common-eval-args.hh @@ -8,11 +8,14 @@ class Store; class EvalState; class Bindings; +template +struct Ptr; + struct MixEvalArgs : virtual Args { MixEvalArgs(); - Bindings * getAutoArgs(EvalState & state); + Ptr getAutoArgs(EvalState & state); Strings searchPath; diff --git a/src/libexpr/eval-inline.hh b/src/libexpr/eval-inline.hh index 492f6f372..f73aaafd2 100644 --- a/src/libexpr/eval-inline.hh +++ b/src/libexpr/eval-inline.hh @@ -27,21 +27,27 @@ LocalNoInlineNoReturn(void throwTypeError(const char * s, const Value & v, const void EvalState::forceValue(Value & v, const Pos & pos) { if (v.type == tThunk) { - Env * env = v.thunk.env; + // FIXME: this is necessary because some values (like vList2) + // are created non-atomically. + Ptr env(v.thunk.env); Expr * expr = v.thunk.expr; try { v.type = tBlackhole; //checkInterrupt(); expr->eval(*this, *env, v); } catch (...) { - v.type = tThunk; v.thunk.env = env; v.thunk.expr = expr; + v.type = tThunk; throw; } } - else if (v.type == tApp) + else if (v.type == tApp) { + // FIXME: idem. + Ptr left(v.app.left); + Ptr right(v.app.right); callFunction(*v.app.left, *v.app.right, v, noPos); + } else if (v.type == tBlackhole) throwEvalError("infinite recursion encountered, at %1%", pos); } diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 784fadf99..5b19e7001 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -166,10 +166,10 @@ static Symbol getName(const AttrName & name, EvalState & state, Env & env) if (name.symbol.set()) { return name.symbol; } else { - Value nameValue; + Root nameValue; name.expr->eval(state, env, nameValue); state.forceStringNoCtx(nameValue); - return state.symbols.create(nameValue.string.s); + return state.symbols.create(nameValue->string.s); } } @@ -384,7 +384,7 @@ Path EvalState::toRealPath(const Path & path, const PathSet & context) Value * EvalState::addConstant(const string & name, Value & v) { - Value * v2 = allocValue(); + auto v2 = allocValue(); *v2 = v; staticBaseEnv.vars[symbols.create(name)] = baseEnvDispl; baseEnv->values[baseEnvDispl++] = v2; @@ -398,11 +398,11 @@ Value * EvalState::addPrimOp(const string & name, size_t arity, PrimOpFun primOp) { if (arity == 0) { - Value v; + Root v; primOp(*this, noPos, nullptr, v); return addConstant(name, v); } - Value * v = allocValue(); + auto v = allocValue(); string name2 = string(name, 0, 2) == "__" ? string(name, 2) : name; Symbol sym = symbols.create(name2); v->type = tPrimOp; @@ -523,12 +523,18 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval) { for (size_t l = var.level; l; --l, env = env->up) ; - if (!var.fromWith) return env->values[var.displ]; + assert(((Object *) env)->type == tEnv); + + if (!var.fromWith) { + auto v = env->values[var.displ]; + if (v) gc.assertObject(v); + return v; + } while (1) { if (env->type == Env::HasWithExpr) { if (noEval) return 0; - Value * v = allocValue(); + auto v = allocValue(); evalAttrs(*env->up, (Expr *) env->values[0], *v); env->values[0] = v; env->type = Env::HasWithAttrs; @@ -536,6 +542,7 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval) Bindings::iterator j = env->values[0]->attrs->find(var.name); if (j != env->values[0]->attrs->end()) { if (countCalls && j->pos) attrSelects[*j->pos]++; + gc.assertObject(j->value); return j->value; } if (!env->prevWith) @@ -559,12 +566,8 @@ Ptr EvalState::allocEnv(size_t size) nrEnvs++; nrValuesInEnvs += size; - auto env = gc.alloc(Env::wordsFor(size), size); - // FIXME - /* We assume that env->values has been cleared by the allocator; maybeThunk() and lookupVar fromWith expect this. */ - - return env; + return gc.alloc(Env::wordsFor(size), size); } @@ -572,14 +575,17 @@ void EvalState::mkList(Value & v, size_t size) { if (size == 0) v.type = tList0; - else if (size == 1) + else if (size == 1) { + v.smallList[0] = nullptr; v.type = tList1; - else if (size == 2) + } else if (size == 2) { + v.smallList[0] = nullptr; + v.smallList[1] = nullptr; v.type = tList2; - else { - v.type = tListN; + } else { v.bigList = gc.alloc>( PtrList::wordsFor(size), tValueList, size); + v.type = tListN; } nrListElems += size; } @@ -616,12 +622,12 @@ void EvalState::mkPos(Value & v, Pos * pos) /* Create a thunk for the delayed computation of the given expression - in the given environment. But if the expression is a variable, - then look it up right away. This significantly reduces the number - of thunks allocated. */ -Value * Expr::maybeThunk(EvalState & state, Env & env) + in the given environment. But if the expression is a variable or a + constant, then look it up right away. This significantly reduces + the number of thunks allocated. */ +Ptr Expr::maybeThunk(EvalState & state, Env & env) { - Value * v = state.allocValue(); + auto v = state.allocValue(); mkThunk(*v, env, this); return v; } @@ -629,9 +635,9 @@ Value * Expr::maybeThunk(EvalState & state, Env & env) unsigned long nrAvoided = 0; -Value * ExprVar::maybeThunk(EvalState & state, Env & env) +Ptr ExprVar::maybeThunk(EvalState & state, Env & env) { - Value * v = state.lookupVar(&env, *this, true); + auto v = state.lookupVar(&env, *this, true); /* The value might not be initialised in the environment yet. In that case, ignore it. */ if (v) { nrAvoided++; return v; } @@ -639,28 +645,28 @@ Value * ExprVar::maybeThunk(EvalState & state, Env & env) } -Value * ExprString::maybeThunk(EvalState & state, Env & env) +Ptr ExprString::maybeThunk(EvalState & state, Env & env) { nrAvoided++; - return &*v; + return v; } -Value * ExprInt::maybeThunk(EvalState & state, Env & env) +Ptr ExprInt::maybeThunk(EvalState & state, Env & env) { nrAvoided++; - return &*v; + return v; } -Value * ExprFloat::maybeThunk(EvalState & state, Env & env) +Ptr ExprFloat::maybeThunk(EvalState & state, Env & env) { nrAvoided++; - return &*v; + return v; } -Value * ExprPath::maybeThunk(EvalState & state, Env & env) +Ptr ExprPath::maybeThunk(EvalState & state, Env & env) { nrAvoided++; - return &*v; + return v; } @@ -723,21 +729,21 @@ void EvalState::eval(Expr * e, Value & v) inline bool EvalState::evalBool(Env & env, Expr * e) { - Value v; + Root v; e->eval(*this, env, v); - if (v.type != tBool) + if (v->type != tBool) throwTypeError("value is %1% while a Boolean was expected", v); - return v.boolean; + return v->boolean; } inline bool EvalState::evalBool(Env & env, Expr * e, const Pos & pos) { - Value v; + Root v; e->eval(*this, env, v); - if (v.type != tBool) + if (v->type != tBool) throwTypeError("value is %1% while a Boolean was expected, at %2%", v, pos); - return v.boolean; + return v->boolean; } @@ -781,14 +787,14 @@ 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; + Env * dynamicEnv = &env; if (recursive) { /* Create a new environment that contains the attributes in this `rec'. */ - Env & env2(state.allocEnv(attrs.size())); - env2.up = &env; - dynamicEnv = &env2; + auto env2 = state.allocEnv(attrs.size()); + env2->up = &env; + dynamicEnv = &*env2; AttrDefs::iterator overrides = attrs.find(state.sOverrides); bool hasOverrides = overrides != attrs.end(); @@ -798,13 +804,13 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v) in the original environment. */ size_t displ = 0; for (auto & i : attrs) { - Value * vAttr; + Ptr vAttr; // FIXME: Ptr unnecessary? if (hasOverrides && !i.second.inherited) { - vAttr = state.allocValue(); + vAttr = state.allocValue(); // FIXME mkThunk(*vAttr, env2, i.second.e); } else vAttr = i.second.e->maybeThunk(state, i.second.inherited ? env : env2); - env2.values[displ++] = vAttr; + env2->values[displ++] = vAttr; v.attrs->push_back(Attr(i.first, vAttr, &i.second.pos)); } @@ -826,7 +832,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v) AttrDefs::iterator j = attrs.find(i.name); if (j != attrs.end()) { (*newBnds)[j->second.displ] = i; - env2.values[j->second.displ] = i.value; + env2->values[j->second.displ] = i.value; } else newBnds->push_back(i); } @@ -841,13 +847,13 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v) /* Dynamic attrs apply *after* rec and __overrides. */ for (auto & i : dynamicAttrs) { - Value nameVal; + Root nameVal; i.nameExpr->eval(state, *dynamicEnv, nameVal); state.forceValue(nameVal, i.pos); - if (nameVal.type == tNull) + if (nameVal->type == tNull) continue; state.forceStringNoCtx(nameVal); - Symbol nameSym = state.symbols.create(nameVal.string.s); + Symbol nameSym = state.symbols.create(nameVal->string.s); Bindings::iterator j = v.attrs->find(nameSym); if (j != v.attrs->end()) throwEvalError("dynamic attribute '%1%' at %2% already defined at %3%", nameSym, i.pos, *j->pos); @@ -864,15 +870,15 @@ void ExprLet::eval(EvalState & state, Env & env, Value & v) { /* Create a new environment that contains the attributes in this `let'. */ - Env & env2(state.allocEnv(attrs->attrs.size())); - env2.up = &env; + auto env2 = state.allocEnv(attrs->attrs.size()); + env2->up = &env; /* The recursive attributes are evaluated in the new environment, while the inherited attributes are evaluated in the original environment. */ size_t displ = 0; for (auto & i : attrs->attrs) - env2.values[displ++] = i.second.e->maybeThunk(state, i.second.inherited ? env : env2); + env2->values[displ++] = i.second.e->maybeThunk(state, i.second.inherited ? env : env2); body->eval(state, env2, v); } @@ -915,9 +921,9 @@ unsigned long nrLookups = 0; void ExprSelect::eval(EvalState & state, Env & env, Value & v) { - Value vTmp; + Root vTmp; Pos * pos2 = 0; - Value * vAttrs = &vTmp; + Value * vAttrs = &*vTmp; e->eval(state, env, vTmp); @@ -960,8 +966,8 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v) void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v) { - Value vTmp; - Value * vAttrs = &vTmp; + Root vTmp; + Value * vAttrs = &*vTmp; e->eval(state, env, vTmp); @@ -994,7 +1000,7 @@ void ExprLambda::eval(EvalState & state, Env & env, Value & v) void ExprApp::eval(EvalState & state, Env & env, Value & v) { /* FIXME: vFun prevents GCC from doing tail call optimisation. */ - Value vFun; + Root vFun; e1->eval(state, env, vFun); state.callFunction(vFun, *(e2->maybeThunk(state, env)), v, pos); } @@ -1028,11 +1034,11 @@ void EvalState::callPrimOp(Value & fun, Value & arg, Value & v, const Pos & pos) if (countCalls) primOpCalls[primOp->primOp->name]++; primOp->primOp->fun(*this, pos, vArgs, v); } else { - Value * fun2 = allocValue(); + auto fun2 = allocValue(); *fun2 = fun; - v.type = tPrimOpApp; v.app.left = fun2; v.app.right = &arg; + v.type = tPrimOpApp; } } @@ -1053,10 +1059,10 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v, const Pos & po * but for functors we may keep a reference, so heap-allocate * a copy and use that instead. */ - auto & fun2 = *allocValue(); - fun2 = fun; + auto fun2 = allocValue(); + *fun2 = fun; /* !!! Should we use the attr pos here? */ - Value v2; + Root v2; callFunction(*found->value, fun2, v2, pos); return callFunction(v2, arg, v, pos); } @@ -1070,19 +1076,19 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v, const Pos & po auto size = (lambda.arg.empty() ? 0 : 1) + (lambda.matchAttrs ? lambda.formals->formals.size() : 0); - Env & env2(allocEnv(size)); - env2.up = fun.lambda.env; + auto env2 = allocEnv(size); + env2->up = fun.lambda.env; size_t displ = 0; if (!lambda.matchAttrs) - env2.values[displ++] = &arg; + env2->values[displ++] = &arg; else { forceAttrs(arg, pos); if (!lambda.arg.empty()) - env2.values[displ++] = &arg; + env2->values[displ++] = &arg; /* For each formal argument, get the actual argument. If there is no matching actual argument but the formal @@ -1093,10 +1099,10 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v, const Pos & po if (j == arg.attrs->end()) { if (!i.def) throwTypeError("%1% called without required argument '%2%', at %3%", lambda, i.name, pos); - env2.values[displ++] = i.def->maybeThunk(*this, env2); + env2->values[displ++] = i.def->maybeThunk(*this, env2); } else { attrsUsed++; - env2.values[displ++] = j->value; + env2->values[displ++] = j->value; } } @@ -1144,7 +1150,7 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res) if (fun.type == tAttrs) { auto found = fun.attrs->find(sFunctor); if (found != fun.attrs->end()) { - Value * v = allocValue(); + auto v = allocValue(); callFunction(*found->value, fun, *v, noPos); forceValue(*v); return autoCallFunction(args, *v, res); @@ -1156,7 +1162,7 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res) return; } - Value * actualArgs = allocValue(); + auto actualArgs = allocValue(); mkAttrs(*actualArgs, fun.lambda.fun->formals->formals.size()); for (auto & i : fun.lambda.fun->formals->formals) { @@ -1175,11 +1181,11 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res) void ExprWith::eval(EvalState & state, Env & env, Value & v) { - Env & env2(state.allocEnv(1)); - env2.up = &env; - env2.prevWith = prevWith; - env2.type = Env::HasWithExpr; - env2.values[0] = (Value *) attrs; + auto env2 = state.allocEnv(1); + env2->up = &env; + env2->prevWith = prevWith; + env2->type = Env::HasWithExpr; + env2->values[0] = (Value *) attrs; body->eval(state, env2, v); } @@ -1207,16 +1213,16 @@ void ExprOpNot::eval(EvalState & state, Env & env, Value & v) void ExprOpEq::eval(EvalState & state, Env & env, Value & v) { - Value v1; e1->eval(state, env, v1); - Value v2; e2->eval(state, env, v2); + Root v1; e1->eval(state, env, v1); + Root v2; e2->eval(state, env, v2); mkBool(v, state.eqValues(v1, v2)); } void ExprOpNEq::eval(EvalState & state, Env & env, Value & v) { - Value v1; e1->eval(state, env, v1); - Value v2; e2->eval(state, env, v2); + Root v1; e1->eval(state, env, v1); + Root v2; e2->eval(state, env, v2); mkBool(v, !state.eqValues(v1, v2)); } @@ -1241,23 +1247,24 @@ void ExprOpImpl::eval(EvalState & state, Env & env, Value & v) void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v) { - Value v1, v2; + Root v1; + Root v2; state.evalAttrs(env, e1, v1); state.evalAttrs(env, e2, v2); state.nrOpUpdates++; - if (v1.attrs->size() == 0) { v = v2; return; } - if (v2.attrs->size() == 0) { v = v1; return; } + 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()); + state.mkAttrs(v, 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. */ - Bindings::iterator i = v1.attrs->begin(); - Bindings::iterator j = v2.attrs->begin(); + Bindings::iterator i = v1->attrs->begin(); + Bindings::iterator j = v2->attrs->begin(); - while (i != v1.attrs->end() && j != v2.attrs->end()) { + while (i != v1->attrs->end() && j != v2->attrs->end()) { if (i->name == j->name) { v.attrs->push_back(*j); ++i; ++j; @@ -1268,8 +1275,8 @@ void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v) v.attrs->push_back(*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()) v.attrs->push_back(*i++); + while (j != v2->attrs->end()) v.attrs->push_back(*j++); state.nrOpUpdateValuesCopied += v.attrs->size(); } @@ -1277,9 +1284,9 @@ void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v) void ExprOpConcatLists::eval(EvalState & state, Env & env, Value & v) { - Value v1; e1->eval(state, env, v1); - Value v2; e2->eval(state, env, v2); - Value * lists[2] = { &v1, &v2 }; + Root v1; e1->eval(state, env, v1); + Root v2; e2->eval(state, env, v2); + Value * lists[2] = { &*v1, &*v2 }; state.concatLists(v, 2, lists, pos); } @@ -1324,7 +1331,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v) Tag firstType = tString; for (auto & i : *es) { - Value vTmp; + Root vTmp; i->eval(state, env, vTmp); /* If the first element is a path, then the result will also @@ -1332,25 +1339,25 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v) since paths are copied when they are used in a derivation), and none of the strings are allowed to have contexts. */ if (first) { - firstType = vTmp.type; + firstType = vTmp->type; first = false; } if (firstType == tInt) { - if (vTmp.type == tInt) { - n += vTmp.integer; - } else if (vTmp.type == tFloat) { + if (vTmp->type == tInt) { + n += vTmp->integer; + } else if (vTmp->type == tFloat) { // Upgrade the type from int to float; firstType = tFloat; nf = n; - nf += vTmp.fpoint; + nf += vTmp->fpoint; } else throwEvalError("cannot add %1% to an integer, at %2%", showType(vTmp), pos); } else if (firstType == tFloat) { - if (vTmp.type == tInt) { - nf += vTmp.integer; - } else if (vTmp.type == tFloat) { - nf += vTmp.fpoint; + if (vTmp->type == tInt) { + nf += vTmp->integer; + } else if (vTmp->type == tFloat) { + nf += vTmp->fpoint; } else throwEvalError("cannot add %1% to a float, at %2%", showType(vTmp), pos); } else @@ -1527,7 +1534,7 @@ string EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context, if (v.type == tAttrs) { auto i = v.attrs->find(sToString); if (i != v.attrs->end()) { - Value v1; + Root v1; callFunction(*i->value, v, v1, pos); return coerceToString(pos, v1, context, coerceMore, copyToStore); } diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 2da34e634..b100f7661 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -42,7 +42,16 @@ struct Env : Object enum { Plain = 0, HasWithExpr, HasWithAttrs } type:2; // FIXME: fold into type? Value * values[0]; - Env(unsigned short size) : Object(tEnv, 0), size(size) {} +private: + + Env(unsigned short size) : Object(tEnv, 0), size(size) { + for (auto i = 0; i < size; i++) + values[i] = nullptr; + } + + friend class GC; + +public: Size words() const { diff --git a/src/libexpr/gc.cc b/src/libexpr/gc.cc index af0cbb693..d24f8a98a 100644 --- a/src/libexpr/gc.cc +++ b/src/libexpr/gc.cc @@ -18,6 +18,15 @@ GC::GC() backSentinel->prev = frontSentinel; backSentinel->next = nullptr; + + frontRootSentinel = (Root *) malloc(sizeof(Root)); + backRootSentinel = (Root *) malloc(sizeof(Root)); + + frontRootSentinel->prev = nullptr; + frontRootSentinel->next = backRootSentinel; + + backRootSentinel->prev = frontRootSentinel; + backRootSentinel->next = nullptr; } GC::~GC() @@ -26,9 +35,18 @@ GC::~GC() for (Ptr * p = frontSentinel->next; p != backSentinel; p = p->next) n++; if (n) - warn("%d GC roots still exist on exit", n); + warn("%d GC root pointers still exist on exit", n); + + n = 0; + for (Root * p = frontRootSentinel->next; p != backRootSentinel; p = p->next) + n++; + if (n) + warn("%d GC root objects still exist on exit", n); + assert(!frontSentinel->prev); assert(!backSentinel->next); + assert(!frontRootSentinel->prev); + assert(!backRootSentinel->next); } void GC::gc() @@ -37,112 +55,120 @@ void GC::gc() std::stack stack; - for (Ptr * p = frontSentinel->next; p != backSentinel; p = p->next) { - if (!p->value) continue; + // FIXME: ensure this gets inlined. + auto push = [&](Object * p) { if (p) { assertObject(p); stack.push(p); } }; - stack.push(p->value); + auto pushPointers = [&](Object * obj) { + switch (obj->type) { + case tFree: + printError("reached a freed object at %x", obj); + abort(); + + case tBindings: { + auto obj2 = (Bindings *) obj; + for (auto i = obj2->attrs; i < obj2->attrs + obj2->size_; ++i) + push(i->value); + break; + } + + case tValueList: { + auto obj2 = (PtrList *) obj; + for (auto i = obj2->elems; i < obj2->elems + obj2->size(); ++i) + push(*i); + break; + } + + case tEnv: { + auto obj2 = (Env *) obj; + push(obj2->up); + if (obj2->type != Env::HasWithExpr) + for (auto i = obj2->values; i < obj2->values + obj2->size; ++i) + push(*i); + break; + } + + case tInt: + case tBool: + case tNull: + case tList0: + case tFloat: + break; + + case tString: + // FIXME + break; + + case tPath: + // FIXME + break; + + case tAttrs: + push(((Value *) obj)->attrs); + break; + + case tList1: + push(((Value *) obj)->smallList[0]); + break; + + case tList2: + push(((Value *) obj)->smallList[0]); + push(((Value *) obj)->smallList[1]); + break; + + case tListN: + push(((Value *) obj)->bigList); + break; + + case tThunk: + case tBlackhole: + push(((Value *) obj)->thunk.env); + break; + + case tApp: + case tPrimOpApp: + push(((Value *) obj)->app.left); + push(((Value *) obj)->app.right); + break; + + case tLambda: + push(((Value *) obj)->lambda.env); + break; + + case tPrimOp: + // FIXME: GC primops? + break; + + default: + printError("don't know how to traverse object at %x (tag %d)", obj, obj->type); + abort(); + } + }; + + auto processStack = [&]() { while (!stack.empty()) { auto obj = stack.top(); stack.pop(); - // FIXME: ensure this gets inlined. - auto push = [&](Object * p) { if (p) { /* FIXME */ assert(isObject(p)); stack.push(p); } }; - //printError("MARK %x", obj); if (!obj->isMarked()) { marked++; obj->mark(); - switch (obj->type) { - - case tFree: - printError("reached a freed object at %x", obj); - abort(); - - case tBindings: { - auto obj2 = (Bindings *) obj; - for (auto i = obj2->attrs; i < obj2->attrs + obj2->size_; ++i) - push(i->value); - break; - } - - case tValueList: { - auto obj2 = (PtrList *) obj; - for (auto i = obj2->elems; i < obj2->elems + obj2->size(); ++i) - push(*i); - break; - } - - case tEnv: { - auto obj2 = (Env *) obj; - push(obj2->up); - if (obj2->type != Env::HasWithExpr) - for (auto i = obj2->values; i < obj2->values + obj2->size; ++i) - push(*i); - break; - } - - case tInt: - case tBool: - case tNull: - case tList0: - case tFloat: - break; - - case tString: - // FIXME - break; - - case tPath: - // FIXME - break; - - case tAttrs: - push(((Value *) obj)->attrs); - break; - - case tList1: - push(((Value *) obj)->smallList[0]); - break; - - case tList2: - push(((Value *) obj)->smallList[0]); - push(((Value *) obj)->smallList[1]); - break; - - case tListN: - push(((Value *) obj)->bigList); - break; - - case tThunk: - push(((Value *) obj)->thunk.env); - break; - - case tApp: - case tPrimOpApp: - push(((Value *) obj)->app.left); - push(((Value *) obj)->app.right); - break; - - case tLambda: - push(((Value *) obj)->lambda.env); - break; - - case tBlackhole: - // FIXME - break; - - case tPrimOp: - // FIXME: GC primops? - break; - - default: - printError("don't know how to traverse object at %x (tag %d)", obj, obj->type); - abort(); - } + pushPointers(obj); } } + }; + + for (Root * p = frontRootSentinel->next; p != backRootSentinel; p = p->next) { + pushPointers(&p->value); + processStack(); + } + + for (Ptr * p = frontSentinel->next; p != backSentinel; p = p->next) { + if (!p->value) continue; + stack.push(p->value); + processStack(); } Size totalObjectsFreed = 0; @@ -162,7 +188,7 @@ void GC::gc() }); } - printError("freed %d bytes in %d dead objects, keeping %d objects", + debug("freed %d bytes in %d dead objects, keeping %d objects", totalWordsFreed * WORD_SIZE, totalObjectsFreed, marked); } @@ -224,6 +250,7 @@ std::pair GC::Arena::freeUnmarked() }; if (tag == tFree) { + //debug("FREE %x %d", obj, obj->getMisc()); if (curFree) { // Merge this object into the previous free // object. @@ -239,10 +266,13 @@ std::pair GC::Arena::freeUnmarked() if (obj->isMarked()) { // Unmark to prepare for the next GC run. + //debug("KEEP OBJECT %x %d %d", obj, obj->type, objSize); curFree = nullptr; obj->unmark(); } else { - //printError("FREE %x %d %d", obj, obj->type, objSize); + //debug("FREE OBJECT %x %d %d", obj, obj->type, objSize); + for (Size i = 0; i < objSize; ++i) + ((Word *) obj)[i] = 0xdeadc0dedeadbeefULL; objectsFreed += 1; wordsFreed += objSize; if (curFree) { @@ -279,9 +309,18 @@ bool GC::isObject(void * p) return false; } -GC::ArenaList::ArenaList() - : nextSize(1024) +void GC::assertObject(void * p) { + if (!isObject(p)) { + printError("object %p is not an object", p); + abort(); + } +} + +GC::ArenaList::ArenaList() +{ + static Size initialHeapSize = std::stol(getEnv("GC_INITIAL_HEAP_SIZE", "1000000")) / WORD_SIZE; + nextSize = initialHeapSize; } } diff --git a/src/libexpr/gc.hh b/src/libexpr/gc.hh index 1b27c96b6..20841d568 100644 --- a/src/libexpr/gc.hh +++ b/src/libexpr/gc.hh @@ -104,94 +104,6 @@ struct PtrList : Object static Size wordsFor(Size size) { return 1 + size; } }; -template -struct Ptr -{ - Ptr * prev = nullptr, * next = nullptr; - T * value = nullptr; - - Ptr() { } - - Ptr(Ptr * next, T * value) : next(next), value(value) - { - assert(value); - assert(next == next->prev->next); - prev = next->prev; - next->prev = this; - prev->next = this; - } - - Ptr(const Ptr & p) - { - if (p.value) { - auto & p2 = const_cast(p); - value = p2.value; - next = &p2; - prev = p2.prev; - prev->next = this; - p2.prev = this; - } - } - - Ptr(Ptr && p) - { - *this = std::move(p); - } - - Ptr & operator =(Ptr && p) - { - reset(); - if (p.value) { - value = p.value; - next = p.next; - prev = p.prev; - p.value = nullptr; - prev->next = this; - next->prev = this; - } - return *this; - } - - Ptr & operator =(const Ptr & p) - { - throw Error("NOT IMPLEMENTED = PTR &"); - } - - ~Ptr() - { - reset(); - } - - void reset() - { - if (value) { - assert(next); - assert(prev); - assert(next->prev == this); - next->prev = prev; - assert(prev->next == this); - prev->next = next; - value = nullptr; - } - } - - T * operator ->() - { - return value; - } - - operator T * () - { - return value; - } - - operator T & () - { - assert(value); - return *value; - } -}; - struct Free : Object { Free * next; @@ -204,6 +116,12 @@ struct Free : Object void setSize(Size size) { assert(size >= 1); setMisc(size); } }; +template +struct Ptr; + +template +struct Root; + struct GC { @@ -212,6 +130,15 @@ private: Ptr * frontSentinel; Ptr * backSentinel; + Root * frontRootSentinel; + Root * backRootSentinel; + + template + friend class Ptr; + + template + friend class Root; + struct Arena { Size size; // in words @@ -317,18 +244,17 @@ public: auto raw = arena.alloc(size); if (raw) { auto obj = new (raw) T(args...); - //printError("ALLOC %x", obj); - return Ptr((Ptr *) frontSentinel->next, obj); + return obj; } } if (i == 0) { - printError("allocation of %d bytes failed, GCing...", size * WORD_SIZE); + debug("allocation of %d bytes failed, GCing...", size * WORD_SIZE); gc(); } else { Size arenaSize = std::max(arenaList.nextSize, size); - arenaList.nextSize = arenaSize * 2; // FIXME: overflow - printError("allocating arena of %d bytes", arenaSize * WORD_SIZE); + arenaList.nextSize = arenaSize * 1.5; // FIXME: overflow + debug("allocating arena of %d bytes", arenaSize * WORD_SIZE); arenaList.arenas.emplace_back(arenaSize); } } @@ -339,8 +265,159 @@ public: void gc(); bool isObject(void * p); + + void assertObject(void * p); }; extern GC gc; +template +struct Ptr +{ +private: + + friend class GC; + + Ptr * prev = nullptr, * next = nullptr; + T * value = nullptr; + + void link() + { + prev = (Ptr *) gc.frontSentinel; + next = prev->next; + next->prev = this; + prev->next = this; + } + +public: + + Ptr() { + link(); + } + + Ptr(T * value) : value(value) + { + link(); + } + + Ptr(const Ptr & p) + { + auto & p2 = const_cast(p); + value = p2.value; + next = &p2; + prev = p2.prev; + prev->next = this; + p2.prev = this; + } + + Ptr(Ptr && p) + { + link(); + value = p.value; + p.value = nullptr; + } + + Ptr & operator =(const Ptr & p) + { + value = p.value; + return *this; + } + + Ptr & operator =(Ptr && p) + { + value = p.value; + p.value = nullptr; + return *this; + } + + Ptr & operator =(T * v) + { + value = v; + return *this; + } + + ~Ptr() + { + assert(next); + assert(prev); + assert(next->prev == this); + next->prev = prev; + assert(prev->next == this); + prev->next = next; + } + + T * operator ->() + { + return value; + } + + T * operator ->() const // FIXME + { + return value; + } + + operator T * () + { + return value; + } + + operator T & () + { + assert(value); + return *value; + } + + operator bool() const + { + return value != nullptr; + } +}; + +template +struct Root +{ + Root * prev = nullptr, * next = nullptr; + T value; + + template + Root(const Args & ... args) + : value{args... } + { + prev = (Root *) gc.frontRootSentinel; + next = prev->next; + next->prev = this; + prev->next = this; + } + + Root(const Root & p) = delete; + Root(Root && p) = delete; + + Root & operator =(const T & v) { value = v; return *this; } + + ~Root() + { + assert(next); + assert(prev); + assert(next->prev == this); + next->prev = prev; + assert(prev->next == this); + prev->next = next; + } + + T * operator ->() + { + return &value; + } + + operator T * () + { + return &value; + } + + operator T & () + { + return value; + } +}; + } diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc index e3a98a47b..967e77f8f 100644 --- a/src/libexpr/get-drvs.cc +++ b/src/libexpr/get-drvs.cc @@ -115,7 +115,7 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool onlyOutputsToInstall) return outputs; /* Check for `meta.outputsToInstall` and return `outputs` reduced to that. */ - const Value * outTI = queryMeta("outputsToInstall"); + const auto outTI = queryMeta("outputsToInstall"); if (!outTI) return outputs; const auto errMsg = Error("this derivation has bad 'meta.outputsToInstall'"); /* ^ this shows during `nix-env -i` right under the bad derivation */ @@ -194,7 +194,7 @@ Value * DrvInfo::queryMeta(const string & name) string DrvInfo::queryMetaString(const string & name) { - Value * v = queryMeta(name); + auto v = queryMeta(name); if (!v || v->type != tString) return ""; return v->string.s; } @@ -202,7 +202,7 @@ string DrvInfo::queryMetaString(const string & name) NixInt DrvInfo::queryMetaInt(const string & name, NixInt def) { - Value * v = queryMeta(name); + auto v = queryMeta(name); if (!v) return def; if (v->type == tInt) return v->integer; if (v->type == tString) { @@ -216,7 +216,7 @@ NixInt DrvInfo::queryMetaInt(const string & name, NixInt def) NixFloat DrvInfo::queryMetaFloat(const string & name, NixFloat def) { - Value * v = queryMeta(name); + auto v = queryMeta(name); if (!v) return def; if (v->type == tFloat) return v->fpoint; if (v->type == tString) { @@ -231,7 +231,7 @@ NixFloat DrvInfo::queryMetaFloat(const string & name, NixFloat def) bool DrvInfo::queryMetaBool(const string & name, bool def) { - Value * v = queryMeta(name); + auto v = queryMeta(name); if (!v) return def; if (v->type == tBool) return v->boolean; if (v->type == tString) { @@ -247,7 +247,7 @@ bool DrvInfo::queryMetaBool(const string & name, bool def) void DrvInfo::setMeta(const string & name, Value * v) { getMeta(); - Bindings * old = meta; + Ptr old = meta; meta = Bindings::allocBindings(1 + (old ? old->size() : 0)); Symbol sym = state->symbols.create(name); if (old) @@ -260,6 +260,7 @@ void DrvInfo::setMeta(const string & name, Value * v) /* Cache for already considered attrsets. */ +// FIXME: Use Ptr? typedef set Done; @@ -320,24 +321,24 @@ static void getDerivations(EvalState & state, Value & vIn, DrvInfos & drvs, Done & done, bool ignoreAssertionFailures) { - Value v; + Root v; state.autoCallFunction(autoArgs, vIn, v); /* Process the expression. */ if (!getDerivation(state, v, pathPrefix, drvs, done, ignoreAssertionFailures)) ; - else if (v.type == tAttrs) { + else if (v->type == tAttrs) { /* !!! undocumented hackery to support combining channels in nix-env.cc. */ - bool combineChannels = v.attrs->find(state.symbols.create("_combineChannels")) != v.attrs->end(); + bool combineChannels = v->attrs->find(state.symbols.create("_combineChannels")) != v->attrs->end(); /* Consider the attributes in sorted order to get more deterministic behaviour in nix-env operations (e.g. when there are names clashes between derivations, the derivation bound to the attribute with the "lower" name should take precedence). */ - for (auto & i : v.attrs->lexicographicOrder()) { + for (auto & i : v->attrs->lexicographicOrder()) { debug("evaluating attribute '%1%'", i->name); if (!std::regex_match(std::string(i->name), attrRegex)) continue; @@ -357,11 +358,11 @@ static void getDerivations(EvalState & state, Value & vIn, } } - else if (v.isList()) { - for (unsigned int n = 0; n < v.listSize(); ++n) { + else if (v->isList()) { + 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); + if (getDerivation(state, *v->listElems()[n], pathPrefix2, drvs, done, ignoreAssertionFailures)) + getDerivations(state, *v->listElems()[n], pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures); } } diff --git a/src/libexpr/get-drvs.hh b/src/libexpr/get-drvs.hh index 526f251c3..8f8466ccf 100644 --- a/src/libexpr/get-drvs.hh +++ b/src/libexpr/get-drvs.hh @@ -26,7 +26,8 @@ private: bool failed = false; // set if we get an AssertionError - Bindings * attrs = nullptr, * meta = nullptr; + Ptr attrs; + Ptr meta; Bindings * getMeta(); diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index 88206789c..82161291f 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -78,7 +78,7 @@ struct Expr virtual void show(std::ostream & str) const; virtual void bindVars(const StaticEnv & env); virtual void eval(EvalState & state, Env & env, Value & v); - virtual Value * maybeThunk(EvalState & state, Env & env); + virtual Ptr maybeThunk(EvalState & state, Env & env); virtual void setName(Symbol & name); }; @@ -98,7 +98,7 @@ struct ExprInt : Expr mkInt(v, n); }; COMMON_METHODS - Value * maybeThunk(EvalState & state, Env & env); + Ptr maybeThunk(EvalState & state, Env & env) override; }; struct ExprFloat : Expr @@ -110,7 +110,7 @@ struct ExprFloat : Expr mkFloat(v, nf); }; COMMON_METHODS - Value * maybeThunk(EvalState & state, Env & env); + Ptr maybeThunk(EvalState & state, Env & env) override; }; struct ExprString : Expr @@ -122,7 +122,7 @@ struct ExprString : Expr mkString(v, s); }; COMMON_METHODS - Value * maybeThunk(EvalState & state, Env & env); + Ptr maybeThunk(EvalState & state, Env & env) override; }; /* Temporary class used during parsing of indented strings. */ @@ -141,7 +141,7 @@ struct ExprPath : Expr mkPathNoCopy(v, this->s.c_str()); }; COMMON_METHODS - Value * maybeThunk(EvalState & state, Env & env); + Ptr maybeThunk(EvalState & state, Env & env) override; }; struct ExprVar : Expr @@ -165,7 +165,7 @@ struct ExprVar : Expr ExprVar(const Symbol & name) : name(name) { }; ExprVar(const Pos & pos, const Symbol & name) : pos(pos), name(name) { }; COMMON_METHODS - Value * maybeThunk(EvalState & state, Env & env); + Ptr maybeThunk(EvalState & state, Env & env) override; }; struct ExprSelect : Expr diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 75c62e153..a8a6d13eb 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -102,14 +102,13 @@ static void prim_scopedImport(EvalState & state, const Pos & pos, Value * * args if (state.store->isStorePath(path) && state.store->isValidPath(path) && isDerivation(path)) { Derivation drv = readDerivation(realPath); - Value & w = *state.allocValue(); + auto w = state.allocValue(); state.mkAttrs(w, 3 + drv.outputs.size()); - Value * v2 = state.allocAttr(w, state.sDrvPath); + auto 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")); + auto outputsVal = state.allocAttr(w, state.symbols.create("outputs")); state.mkList(*outputsVal, drv.outputs.size()); unsigned int outputs_index = 0; @@ -119,8 +118,8 @@ static void prim_scopedImport(EvalState & state, const Pos & pos, Value * * args outputsVal->listElems()[outputs_index] = state.allocValue(); mkString(*(outputsVal->listElems()[outputs_index++]), o.first); } - w.attrs->sort(); - Value fun; + w->attrs->sort(); + Root fun; state.evalFile(settings.nixDataDir + "/nix/corepkgs/imported-drv-to-derivation.nix", fun); state.forceFunction(fun, pos); mkApp(v, fun, w); @@ -355,7 +354,6 @@ typedef list> ValueList; static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * args, Value & v) { -#if 0 state.forceAttrs(*args[0], pos); /* Get the start set. */ @@ -381,10 +379,10 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar no new elements are found. */ ValueList res; // `doneKeys' doesn't need to be a GC root, because its values are - // reachable from res. + // reachable from res. FIXME: dubious. set doneKeys; while (!workSet.empty()) { - Value * e = *(workSet.begin()); + auto e = *(workSet.begin()); workSet.pop_front(); state.forceAttrs(*e, pos); @@ -395,19 +393,19 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar throw EvalError(format("attribute 'key' required, at %1%") % pos); state.forceValue(*key->value); - if (doneKeys.find(key->value) != doneKeys.end()) continue; + if (doneKeys.count(key->value)) continue; doneKeys.insert(key->value); res.push_back(e); /* Call the `operator' function with `e' as argument. */ - Value call; + Root call; mkApp(call, *op->value, *e); 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]); - workSet.push_back(call.listElems()[n]); + for (unsigned int n = 0; n < call->listSize(); ++n) { + state.forceValue(*call->listElems()[n]); + workSet.push_back(call->listElems()[n]); } } @@ -416,8 +414,6 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar unsigned int n = 0; for (auto & i : res) v.listElems()[n++] = i; -#endif - abort(); } @@ -940,7 +936,7 @@ static void prim_readDir(EvalState & state, const Pos & pos, Value * * args, Val state.mkAttrs(v, entries.size()); for (auto & ent : entries) { - Value * ent_val = state.allocAttr(v, state.symbols.create(ent.name)); + auto ent_val = state.allocAttr(v, state.symbols.create(ent.name)); if (ent.type == DT_UNKNOWN) ent.type = getFileType(path + "/" + ent.name); mkStringNoCopy(*ent_val, @@ -1030,20 +1026,20 @@ static void addPath(EvalState & state, const Pos & pos, const string & name, con /* Call the filter function. The first argument is the path, the second is a string indicating the type of the file. */ - Value arg1; + auto arg1 = state.allocValue(); mkString(arg1, path); - Value fun2; + Root fun2; state.callFunction(*filterFun, arg1, fun2, noPos); - Value arg2; + auto arg2 = state.allocValue(); mkString(arg2, S_ISREG(st.st_mode) ? "regular" : S_ISDIR(st.st_mode) ? "directory" : S_ISLNK(st.st_mode) ? "symlink" : "unknown" /* not supported, will fail! */); - Value res; + Root res; state.callFunction(fun2, arg2, res, noPos); return state.forceBool(res, pos); @@ -1355,8 +1351,8 @@ static void prim_mapAttrs(EvalState & state, const Pos & pos, Value * * args, Va state.mkAttrs(v, args[1]->attrs->size()); for (auto & i : *args[1]->attrs) { - Value * vName = state.allocValue(); - Value * vFun2 = state.allocValue(); + auto vName = state.allocValue(); + auto vFun2 = state.allocValue(); mkString(*vName, i.name); mkApp(*vFun2, *args[0], *vName); mkApp(*state.allocAttr(v, i.name), *vFun2, *i.value); @@ -1443,7 +1439,7 @@ static void prim_filter(EvalState & state, const Pos & pos, Value * * args, Valu bool same = true; for (unsigned int n = 0; n < args[1]->listSize(); ++n) { - Value res; + Root res; state.callFunction(*args[0], *args[1]->listElems()[n], res, noPos); if (state.forceBool(res, pos)) vs[k++] = args[1]->listElems()[n]; @@ -1498,12 +1494,12 @@ static void prim_foldlStrict(EvalState & state, const Pos & pos, Value * * args, state.forceList(*args[2], pos); if (args[2]->listSize()) { - Value * vCur = args[1]; + Ptr vCur = args[1]; for (unsigned int n = 0; n < args[2]->listSize(); ++n) { - Value vTmp; + Root vTmp; state.callFunction(*args[0], *vCur, vTmp, pos); - vCur = n == args[2]->listSize() - 1 ? &v : state.allocValue(); + vCur = n == args[2]->listSize() - 1 ? Ptr(&v) : state.allocValue(); state.callFunction(vTmp, *args[2]->listElems()[n], *vCur, pos); } state.forceValue(v); @@ -1519,7 +1515,7 @@ static void anyOrAll(bool any, EvalState & state, const Pos & pos, Value * * arg state.forceFunction(*args[0], pos); state.forceList(*args[1], pos); - Value vTmp; + Root vTmp; for (unsigned int n = 0; n < args[1]->listSize(); ++n) { state.callFunction(*args[0], *args[1]->listElems()[n], vTmp, pos); bool res = state.forceBool(vTmp, pos); @@ -1555,7 +1551,7 @@ 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(); + auto arg = state.allocValue(); mkInt(*arg, n); mkApp(*(v.listElems()[n] = state.allocValue()), *args[0], *arg); } @@ -1584,7 +1580,7 @@ static void prim_sort(EvalState & state, const Pos & pos, Value * * args, Value if (args[0]->type == tPrimOp && args[0]->primOp->fun == prim_lessThan) return CompareValues()(a, b); - Value vTmp1, vTmp2; + Root vTmp1, vTmp2; state.callFunction(*args[0], *a, vTmp1, pos); state.callFunction(vTmp1, *b, vTmp2, pos); return state.forceBool(vTmp2, pos); @@ -1610,7 +1606,7 @@ static void prim_partition(EvalState & state, const Pos & pos, Value * * args, V for (unsigned int n = 0; n < len; ++n) { auto vElem = args[1]->listElems()[n]; state.forceValue(*vElem); - Value res; + Root res; state.callFunction(*args[0], *vElem, res, pos); if (state.forceBool(res, pos)) right.push_back(vElem); @@ -1620,13 +1616,13 @@ static void prim_partition(EvalState & state, const Pos & pos, Value * * args, V state.mkAttrs(v, 2); - Value * vRight = state.allocAttr(v, state.sRight); + auto vRight = state.allocAttr(v, state.sRight); auto rsize = right.size(); state.mkList(*vRight, rsize); if (rsize) memcpy(vRight->listElems(), right.data(), sizeof(Value *) * rsize); - Value * vWrong = state.allocAttr(v, state.sWrong); + auto vWrong = state.allocAttr(v, state.sWrong); auto wsize = wrong.size(); state.mkList(*vWrong, wsize); if (wsize) @@ -1644,22 +1640,23 @@ static void prim_concatMap(EvalState & state, const Pos & pos, Value * * args, V state.forceList(*args[1], pos); auto nrLists = args[1]->listSize(); - Value lists[nrLists]; + // FIXME: Root<>[] is inefficient + Root lists[nrLists]; size_t len = 0; for (unsigned int n = 0; n < nrLists; ++n) { Value * vElem = args[1]->listElems()[n]; state.callFunction(*args[0], *vElem, lists[n], pos); state.forceList(lists[n], pos); - len += lists[n].listSize(); + len += lists[n]->listSize(); } state.mkList(v, len); auto out = v.listElems(); for (unsigned int n = 0, pos = 0; n < nrLists; ++n) { - auto l = lists[n].listSize(); + auto l = lists[n]->listSize(); if (l) - memcpy(out + pos, lists[n].listElems(), l * sizeof(Value *)); + memcpy(out + pos, lists[n]->listElems(), l * sizeof(Value *)); pos += l; } } @@ -2130,7 +2127,7 @@ void EvalState::createBaseEnv() auto vThrow = addPrimOp("throw", 1, prim_throw); auto addPurityError = [&](const std::string & name) { - Value * v2 = allocValue(); + auto v2 = allocValue(); mkString(*v2, fmt("'%s' is not allowed in pure evaluation mode", name)); mkApp(v, *vThrow, *v2); addConstant(name, v); @@ -2161,7 +2158,7 @@ void EvalState::createBaseEnv() // Miscellaneous auto vScopedImport = addPrimOp("scopedImport", 2, prim_scopedImport); - Value * v2 = allocValue(); + auto v2 = allocValue(); mkAttrs(*v2, 0); mkApp(v, *vScopedImport, *v2); forceValue(v); diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh index ee0008996..187ddc0fb 100644 --- a/src/libexpr/value.hh +++ b/src/libexpr/value.hh @@ -28,7 +28,8 @@ typedef double NixFloat; class ExternalValueBase { friend std::ostream & operator << (std::ostream & str, const ExternalValueBase & v); - protected: + +protected: /* Print out the value */ virtual std::ostream & print(std::ostream & str) const = 0; @@ -121,7 +122,14 @@ struct Value : Object NixFloat fpoint; }; - Value() : Object(tBlackhole, 0) { } +private: + + Value() : Object(tNull, 0) {} + + friend class GC; + template friend class Root; + +public: bool isList() const { diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc index 618895d38..8fd5acca8 100755 --- a/src/nix-build/nix-build.cc +++ b/src/nix-build/nix-build.cc @@ -245,7 +245,7 @@ static void _main(int argc, char * * argv) auto state = std::make_unique(myArgs.searchPath, store); state->repair = repair; - Bindings & autoArgs = *myArgs.getAutoArgs(*state); + auto autoArgs = myArgs.getAutoArgs(*state); if (packages) { std::ostringstream joined; @@ -293,13 +293,13 @@ static void _main(int argc, char * * argv) if (attrPaths.empty()) attrPaths = {""}; for (auto e : exprs) { - Value vRoot; + Root vRoot; state->eval(e, vRoot); for (auto & i : attrPaths) { - Value & v(*findAlongAttrPath(*state, i, autoArgs, vRoot)); + auto v = findAlongAttrPath(*state, i, *autoArgs, vRoot); state->forceValue(v); - getDerivations(*state, v, "", autoArgs, drvs, false); + getDerivations(*state, v, "", *autoArgs, drvs, false); } } @@ -339,7 +339,7 @@ static void _main(int argc, char * * argv) try { auto expr = state->parseExprFromString("(import {}).bashInteractive", absPath(".")); - Value v; + Root v; state->eval(expr, v); auto drv = getDerivation(*state, v, false); diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc index 56ed75dae..d1f99629d 100644 --- a/src/nix-env/nix-env.cc +++ b/src/nix-env/nix-env.cc @@ -46,7 +46,7 @@ struct InstallSourceInfo Path nixExprPath; /* for srcNixExprDrvs, srcNixExprs */ Path profile; /* for srcProfile */ string systemFilter; /* for srcNixExprDrvs */ - Bindings * autoArgs; + Ptr autoArgs; }; @@ -131,7 +131,7 @@ static void getAllExprs(EvalState & state, attrs.insert(attrName); /* Load the expression on demand. */ Value & vFun = state.getBuiltin("import"); - Value & vArg(*state.allocValue()); + auto vArg = state.allocValue(); mkString(vArg, path2); if (v.attrs->size() == v.attrs->capacity()) throw Error(format("too many Nix expressions in directory '%1%'") % path); @@ -176,10 +176,10 @@ static void loadDerivations(EvalState & state, Path nixExprPath, string systemFilter, Bindings & autoArgs, const string & pathPrefix, DrvInfos & elems) { - Value vRoot; + auto vRoot = state.allocValue(); loadSourceExpr(state, nixExprPath, vRoot); - Value & v(*findAlongAttrPath(state, pathPrefix, autoArgs, vRoot)); + auto v = findAlongAttrPath(state, pathPrefix, autoArgs, vRoot); getDerivations(state, v, pathPrefix, autoArgs, elems, true); @@ -360,13 +360,14 @@ static void queryInstSources(EvalState & state, (import ./foo.nix)' = `(import ./foo.nix).bar'. */ case srcNixExprs: { - Value vArg; + auto vArg = state.allocValue(); loadSourceExpr(state, instSource.nixExprPath, vArg); for (auto & i : args) { Expr * eFun = state.parseExprFromString(i, absPath(".")); - Value vFun, vTmp; + auto vFun = state.allocValue(); state.eval(eFun, vFun); + auto vTmp = state.allocValue(); mkApp(vTmp, vFun, vArg); getDerivations(state, vTmp, "", *instSource.autoArgs, elems, true); } @@ -416,10 +417,10 @@ static void queryInstSources(EvalState & state, } case srcAttrPath: { - Value vRoot; + Root vRoot; loadSourceExpr(state, instSource.nixExprPath, vRoot); for (auto & i : args) { - Value & v(*findAlongAttrPath(state, i, *instSource.autoArgs, vRoot)); + auto v = findAlongAttrPath(state, i, *instSource.autoArgs, vRoot); getDerivations(state, v, "", *instSource.autoArgs, elems, true); } break; @@ -639,7 +640,7 @@ static void opUpgrade(Globals & globals, Strings opFlags, Strings opArgs) static void setMetaFlag(EvalState & state, DrvInfo & drv, const string & name, const string & value) { - Value * v = state.allocValue(); + auto v = state.allocValue(); mkString(*v, value.c_str()); drv.setMeta(name, v); } diff --git a/src/nix-env/user-env.cc b/src/nix-env/user-env.cc index 9c021a263..4222e48c6 100644 --- a/src/nix-env/user-env.cc +++ b/src/nix-env/user-env.cc @@ -17,7 +17,7 @@ DrvInfos queryInstalled(EvalState & state, const Path & userEnv) DrvInfos elems; Path manifestFile = userEnv + "/manifest.nix"; if (pathExists(manifestFile)) { - Value v; + Root v; state.evalFile(manifestFile, v); auto bindings = Bindings::allocBindings(0); getDerivations(state, v, "", bindings, elems, false); @@ -105,26 +105,27 @@ bool createUserEnv(EvalState & state, DrvInfos & elems, fmt("%1%", (Value &) manifest), references); /* Get the environment builder expression. */ - Value envBuilder; + auto envBuilder = state.allocValue(); state.evalFile(state.findFile("nix/buildenv.nix"), envBuilder); /* Construct a Nix expression that calls the user environment builder with the manifest as argument. */ - Value args, topLevel; + auto args = state.allocValue(); + Root topLevel; state.mkAttrs(args, 3); mkString(*state.allocAttr(args, state.symbols.create("manifest")), manifestFile, {manifestFile}); - args.attrs->push_back(Attr(state.symbols.create("derivations"), (Value *) manifest)); - args.attrs->sort(); + args->attrs->push_back(Attr(state.symbols.create("derivations"), (Value *) manifest)); + args->attrs->sort(); mkApp(topLevel, envBuilder, args); /* Evaluate it. */ debug("evaluating user environment builder"); state.forceValue(topLevel); PathSet context; - Attr & aDrvPath(*topLevel.attrs->find(state.sDrvPath)); + Attr & aDrvPath(*topLevel->attrs->find(state.sDrvPath)); Path topLevelDrv = state.coerceToPath(aDrvPath.pos ? *(aDrvPath.pos) : noPos, *(aDrvPath.value), context); - Attr & aOutPath(*topLevel.attrs->find(state.sOutPath)); + Attr & aOutPath(*topLevel->attrs->find(state.sOutPath)); Path topLevelOut = state.coerceToPath(aOutPath.pos ? *(aOutPath.pos) : noPos, *(aOutPath.value), context); /* Realise the resulting store expression. */ diff --git a/src/nix-instantiate/nix-instantiate.cc b/src/nix-instantiate/nix-instantiate.cc index a736caa8f..e7038e831 100644 --- a/src/nix-instantiate/nix-instantiate.cc +++ b/src/nix-instantiate/nix-instantiate.cc @@ -35,18 +35,18 @@ void processExpr(EvalState & state, const Strings & attrPaths, return; } - Value vRoot; + Root vRoot; state.eval(e, vRoot); for (auto & i : attrPaths) { - Value & v(*findAlongAttrPath(state, i, autoArgs, vRoot)); + auto v = findAlongAttrPath(state, i, autoArgs, vRoot); state.forceValue(v); PathSet context; if (evalOnly) { - Value vRes; + Root vRes; if (autoArgs.empty()) - vRes = v; + vRes = *v; else state.autoCallFunction(autoArgs, v, vRes); if (output == okXML) @@ -55,7 +55,7 @@ void processExpr(EvalState & state, const Strings & attrPaths, printValueAsJSON(state, strict, vRes, std::cout, context); else { if (strict) state.forceValueDeep(vRes); - std::cout << vRes << std::endl; + std::cout << *vRes << std::endl; } } else { DrvInfos drvs; @@ -159,7 +159,7 @@ static int _main(int argc, char * * argv) auto state = std::make_unique(myArgs.searchPath, store); state->repair = repair; - Bindings & autoArgs = *myArgs.getAutoArgs(*state); + auto autoArgs = myArgs.getAutoArgs(*state); if (attrPaths.empty()) attrPaths = {""}; @@ -174,7 +174,7 @@ static int _main(int argc, char * * argv) if (readStdin) { Expr * e = state->parseStdin(); - processExpr(*state, attrPaths, parseOnly, strict, autoArgs, + processExpr(*state, attrPaths, parseOnly, strict, *autoArgs, evalOnly, outputKind, xmlOutputSourceLocation, e); } else if (files.empty() && !fromArgs) files.push_back("./default.nix"); @@ -183,7 +183,7 @@ static int _main(int argc, char * * argv) Expr * e = fromArgs ? state->parseExprFromString(i, absPath(".")) : state->parseExprFromFile(resolveExprPath(state->checkSourcePath(lookupFileArg(*state, i)))); - processExpr(*state, attrPaths, parseOnly, strict, autoArgs, + processExpr(*state, attrPaths, parseOnly, strict, *autoArgs, evalOnly, outputKind, xmlOutputSourceLocation, e); } diff --git a/src/nix-prefetch-url/nix-prefetch-url.cc b/src/nix-prefetch-url/nix-prefetch-url.cc index f54706a8a..b77f2dee6 100644 --- a/src/nix-prefetch-url/nix-prefetch-url.cc +++ b/src/nix-prefetch-url/nix-prefetch-url.cc @@ -30,12 +30,12 @@ string resolveMirrorUri(EvalState & state, string uri) if (p == string::npos) throw Error("invalid mirror URI"); string mirrorName(s, 0, p); - Value vMirrors; + Root vMirrors; state.eval(state.parseExprFromString("import ", "."), vMirrors); state.forceAttrs(vMirrors); - auto mirrorList = vMirrors.attrs->find(state.symbols.create(mirrorName)); - if (mirrorList == vMirrors.attrs->end()) + auto mirrorList = vMirrors->attrs->find(state.symbols.create(mirrorName)); + if (mirrorList == vMirrors->attrs->end()) throw Error(format("unknown mirror name '%1%'") % mirrorName); state.forceList(*mirrorList->value); @@ -106,7 +106,7 @@ static int _main(int argc, char * * argv) auto store = openStore(); auto state = std::make_unique(myArgs.searchPath, store); - Bindings & autoArgs = *myArgs.getAutoArgs(*state); + auto autoArgs = myArgs.getAutoArgs(*state); /* If -A is given, get the URI from the specified Nix expression. */ @@ -117,14 +117,14 @@ static int _main(int argc, char * * argv) uri = args[0]; } else { Path path = resolveExprPath(lookupFileArg(*state, args.empty() ? "." : args[0])); - Value vRoot; + Root vRoot; state->evalFile(path, vRoot); - Value & v(*findAlongAttrPath(*state, attrPath, autoArgs, vRoot)); + auto v = findAlongAttrPath(*state, attrPath, *autoArgs, vRoot); state->forceAttrs(v); /* Extract the URI. */ - auto attr = v.attrs->find(state->symbols.create("urls")); - if (attr == v.attrs->end()) + auto attr = v->attrs->find(state->symbols.create("urls")); + if (attr == v->attrs->end()) throw Error("attribute set does not contain a 'urls' attribute"); state->forceList(*attr->value); if (attr->value->listSize() < 1) @@ -132,16 +132,16 @@ static int _main(int argc, char * * argv) uri = state->forceString(*attr->value->listElems()[0]); /* Extract the hash mode. */ - attr = v.attrs->find(state->symbols.create("outputHashMode")); - if (attr == v.attrs->end()) + attr = v->attrs->find(state->symbols.create("outputHashMode")); + if (attr == v->attrs->end()) printInfo("warning: this does not look like a fetchurl call"); else unpack = state->forceString(*attr->value) == "recursive"; /* Extract the name. */ if (name.empty()) { - attr = v.attrs->find(state->symbols.create("name")); - if (attr != v.attrs->end()) + attr = v->attrs->find(state->symbols.create("name")); + if (attr != v->attrs->end()) name = state->forceString(*attr->value); } } diff --git a/src/nix/command.hh b/src/nix/command.hh index 97a6fee7f..c362f9daa 100644 --- a/src/nix/command.hh +++ b/src/nix/command.hh @@ -1,6 +1,7 @@ #pragma once #include "args.hh" +#include "gc.hh" #include "common-eval-args.hh" namespace nix { @@ -66,7 +67,7 @@ struct Installable Buildable toBuildable(); - virtual Value * toValue(EvalState & state) + virtual Ptr toValue(EvalState & state) { throw Error("argument '%s' cannot be evaluated", what()); } @@ -82,7 +83,7 @@ struct SourceExprCommand : virtual Args, StoreCommand, MixEvalArgs are installing. This is either the file specified by ‘--file’, or an attribute set constructed from $NIX_PATH, e.g. ‘{ nixpkgs = import ...; bla = import ...; }’. */ - Value * getSourceExpr(EvalState & state); + Ptr getSourceExpr(EvalState & state); ref getEvalState(); @@ -90,7 +91,7 @@ private: std::shared_ptr evalState; - Value * vSourceExpr = 0; + Ptr vSourceExpr; }; enum RealiseMode { Build, NoBuild, DryRun }; diff --git a/src/nix/edit.cc b/src/nix/edit.cc index 7a356236f..a347a5597 100644 --- a/src/nix/edit.cc +++ b/src/nix/edit.cc @@ -36,7 +36,7 @@ struct CmdEdit : InstallableCommand auto v = installable->toValue(*state); - Value * v2; + Ptr v2; try { auto dummyArgs = Bindings::allocBindings(0); v2 = findAlongAttrPath(*state, "meta.position", dummyArgs, *v); diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 0c1ad3ab3..424aacd41 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -22,7 +22,7 @@ SourceExprCommand::SourceExprCommand() .dest(&file); } -Value * SourceExprCommand::getSourceExpr(EvalState & state) +Ptr SourceExprCommand::getSourceExpr(EvalState & state) { if (vSourceExpr) return vSourceExpr; @@ -57,9 +57,9 @@ Value * SourceExprCommand::getSourceExpr(EvalState & state) state.getBuiltin("import"), mkString(*state.allocValue(), res.second)); #endif - Value * v1 = state.allocValue(); + auto v1 = state.allocValue(); mkPrimOpApp(*v1, state.getBuiltin("findFile"), state.getBuiltin("nixPath")); - Value * v2 = state.allocValue(); + auto v2 = state.allocValue(); mkApp(*v2, *v1, mkString(*state.allocValue(), i.first)); mkApp(*state.allocAttr(*vSourceExpr, state.symbols.create(i.first)), state.getBuiltin("import"), *v2); @@ -155,7 +155,7 @@ struct InstallableExpr : InstallableValue std::string what() override { return text; } - Value * toValue(EvalState & state) override + Ptr toValue(EvalState & state) override { auto v = state.allocValue(); state.eval(state.parseExprFromString(text, absPath(".")), *v); @@ -173,13 +173,13 @@ struct InstallableAttrPath : InstallableValue std::string what() override { return attrPath; } - Value * toValue(EvalState & state) override + Ptr toValue(EvalState & state) override { auto source = cmd.getSourceExpr(state); - Bindings & autoArgs = *cmd.getAutoArgs(state); + auto autoArgs = cmd.getAutoArgs(state); - Value * v = findAlongAttrPath(state, attrPath, autoArgs, *source); + auto v = findAlongAttrPath(state, attrPath, *autoArgs, *source); state.forceValue(*v); return v; diff --git a/src/nix/repl.cc b/src/nix/repl.cc index ac6f11a33..5df291ae3 100644 --- a/src/nix/repl.cc +++ b/src/nix/repl.cc @@ -38,7 +38,7 @@ struct NixRepl { string curDir; EvalState state; - Bindings * autoArgs; + Ptr autoArgs; Strings loadedFiles; @@ -304,11 +304,11 @@ StringSet NixRepl::completePrefix(string prefix) string cur2 = string(cur, dot + 1); Expr * e = parseString(expr); - Value v; + Root v; e->eval(state, *env, v); state.forceAttrs(v); - for (auto & i : *v.attrs) { + for (auto & i : *v->attrs) { string name = i.name; if (string(name, 0, cur2.size()) != cur2) continue; completions.insert(prev + expr + "." + name); @@ -404,7 +404,7 @@ bool NixRepl::processLine(string line) } else if (command == ":a" || command == ":add") { - Value v; + Root v; evalString(arg, v); addAttrsToScope(v); } @@ -420,12 +420,12 @@ bool NixRepl::processLine(string line) } else if (command == ":t") { - Value v; + Root v; evalString(arg, v); std::cout << showType(v) << std::endl; } else if (command == ":u") { - Value v, f, result; + Root v, f, result; evalString(arg, v); evalString("drv: (import {}).runCommand \"shell\" { buildInputs = [ drv ]; } \"\"", f); state.callFunction(f, v, result, Pos()); @@ -435,7 +435,7 @@ bool NixRepl::processLine(string line) } else if (command == ":b" || command == ":i" || command == ":s") { - Value v; + Root v; evalString(arg, v); Path drvPath = getDerivationPath(v); @@ -457,7 +457,7 @@ bool NixRepl::processLine(string line) } else if (command == ":p" || command == ":print") { - Value v; + Root v; evalString(arg, v); printValue(std::cout, v, 1000000000) << std::endl; } @@ -483,7 +483,7 @@ bool NixRepl::processLine(string line) v.thunk.expr = e; addVarToScope(state.symbols.create(name), v); } else { - Value v; + Root v; evalString(line, v); printValue(std::cout, v, 1) << std::endl; } @@ -497,9 +497,9 @@ void NixRepl::loadFile(const Path & path) { loadedFiles.remove(path); loadedFiles.push_back(path); - Value v, v2; + Root v, v2; state.evalFile(lookupFileArg(state, path), v); - state.autoCallFunction(*autoArgs, v, v2); + state.autoCallFunction(autoArgs, v, v2); addAttrsToScope(v2); } diff --git a/src/nix/search.cc b/src/nix/search.cc index e60c468b9..0d07103bc 100644 --- a/src/nix/search.cc +++ b/src/nix/search.cc @@ -235,12 +235,12 @@ struct CmdSearch : SourceExprCommand, MixJSON warn("using cached results; pass '-u' to update the cache"); - Value vRoot; + Root vRoot; parseJSON(*state, readFile(jsonCacheFileName), vRoot); fromCache = true; - doExpr(&vRoot, "", true, nullptr); + doExpr(&*vRoot, "", true, nullptr); } else { diff --git a/tests/common.sh.in b/tests/common.sh.in index 6a523ca9d..d1557107a 100644 --- a/tests/common.sh.in +++ b/tests/common.sh.in @@ -35,6 +35,9 @@ export HAVE_SODIUM="@HAVE_SODIUM@" export version=@PACKAGE_VERSION@ export system=@system@ +# Make it more likely to trigger GC bugs. +export GC_INITIAL_HEAP_SIZE=10 + cacheDir=$TEST_ROOT/binary-cache readLink() {