From 49b0bb020663b7283549754a826b1346c93a8bbb Mon Sep 17 00:00:00 2001 From: Kevin Amado Date: Sat, 27 Nov 2021 12:40:24 -0500 Subject: [PATCH 1/5] 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/libcmd/installables.cc | 11 ++++++----- 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 ++-- src/nix-build/nix-build.cc | 2 +- src/nix-env/user-env.cc | 2 +- src/nix-instantiate/nix-instantiate.cc | 2 +- src/nix/eval.cc | 2 +- src/nix/repl.cc | 4 ++-- 12 files changed, 28 insertions(+), 26 deletions(-) diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index 0c2db0399..268861ce8 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -198,8 +198,9 @@ void SourceExprCommand::completeInstallable(std::string_view prefix) prefix_ = ""; } - Value &v1(*findAlongAttrPath(*state, prefix_, *autoArgs, root).first); - state->forceValue(v1); + auto [v, pos] = findAlongAttrPath(*state, prefix_, *autoArgs, root); + Value &v1(*v); + state->forceValue(v1, pos); Value v2; state->autoCallFunction(*autoArgs, v1, v2); @@ -446,7 +447,7 @@ struct InstallableAttrPath : InstallableValue std::pair toValue(EvalState & state) override { auto [vRes, pos] = findAlongAttrPath(state, attrPath, *cmd.getAutoArgs(state), **v); - state.forceValue(*vRes); + state.forceValue(*vRes, pos); return {vRes, pos}; } @@ -496,7 +497,7 @@ Value * InstallableFlake::getFlakeOutputs(EvalState & state, const flake::Locked auto aOutputs = vFlake->attrs->get(state.symbols.create("outputs")); assert(aOutputs); - state.forceValue(*aOutputs->value); + state.forceValue(*aOutputs->value, aOutputs->value->determinePos(noPos)); return aOutputs->value; } @@ -608,7 +609,7 @@ std::pair InstallableFlake::toValue(EvalState & state) for (auto & attrPath : getActualAttrPaths()) { try { auto [v, pos] = findAlongAttrPath(state, attrPath, *emptyArgs, *vOutputs); - state.forceValue(*v); + state.forceValue(*v, pos); return {v, pos}; } catch (AttrPathNotFound & e) { } 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 = diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc index 471a03ac7..66aa145a4 100755 --- a/src/nix-build/nix-build.cc +++ b/src/nix-build/nix-build.cc @@ -318,7 +318,7 @@ static void main_nix_build(int argc, char * * argv) for (auto & i : attrPaths) { Value & v(*findAlongAttrPath(*state, i, *autoArgs, vRoot).first); - state->forceValue(v); + state->forceValue(v, v.determinePos(noPos)); getDerivations(*state, v, "", *autoArgs, drvs, false); } } diff --git a/src/nix-env/user-env.cc b/src/nix-env/user-env.cc index 68c1c16b2..727338105 100644 --- a/src/nix-env/user-env.cc +++ b/src/nix-env/user-env.cc @@ -129,7 +129,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems, /* Evaluate it. */ debug("evaluating user environment builder"); - state.forceValue(topLevel); + state.forceValue(topLevel, topLevel.determinePos(noPos)); PathSet context; Attr & aDrvPath(*topLevel.attrs->find(state.sDrvPath)); auto topLevelDrv = state.store->parseStorePath(state.coerceToPath(*aDrvPath.pos, *aDrvPath.value, context)); diff --git a/src/nix-instantiate/nix-instantiate.cc b/src/nix-instantiate/nix-instantiate.cc index 19a954ddd..8836f810c 100644 --- a/src/nix-instantiate/nix-instantiate.cc +++ b/src/nix-instantiate/nix-instantiate.cc @@ -40,7 +40,7 @@ void processExpr(EvalState & state, const Strings & attrPaths, for (auto & i : attrPaths) { Value & v(*findAlongAttrPath(state, i, autoArgs, vRoot).first); - state.forceValue(v); + state.forceValue(v, v.determinePos(noPos)); PathSet context; if (evalOnly) { diff --git a/src/nix/eval.cc b/src/nix/eval.cc index c7517cf79..686244be0 100644 --- a/src/nix/eval.cc +++ b/src/nix/eval.cc @@ -81,7 +81,7 @@ struct CmdEval : MixJSON, InstallableCommand recurse = [&](Value & v, const Pos & pos, const Path & path) { - state->forceValue(v); + state->forceValue(v, pos); if (v.type() == nString) // FIXME: disallow strings with contexts? writeFile(path, v.string.s); diff --git a/src/nix/repl.cc b/src/nix/repl.cc index be9f96836..5c2bf4bfd 100644 --- a/src/nix/repl.cc +++ b/src/nix/repl.cc @@ -712,7 +712,7 @@ void NixRepl::evalString(string s, Value & v) { Expr * e = parseString(s); e->eval(*state, *env, v); - state->forceValue(v); + state->forceValue(v, v.determinePos(noPos)); } @@ -742,7 +742,7 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m str.flush(); checkInterrupt(); - state->forceValue(v); + state->forceValue(v, v.determinePos(noPos)); switch (v.type()) { From 1472e045a757af4e289801673b456b78ebf18933 Mon Sep 17 00:00:00 2001 From: Kevin Amado Date: Fri, 21 Jan 2022 10:04:09 -0500 Subject: [PATCH 2/5] avoid unnecesary calls --- src/libexpr/eval-inline.hh | 16 ---------------- src/libexpr/eval.hh | 2 -- 2 files changed, 18 deletions(-) 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); From c3896e19d0001b4f729017aa96d0a44b6e479a52 Mon Sep 17 00:00:00 2001 From: Kevin Amado Date: Fri, 21 Jan 2022 10:43:16 -0500 Subject: [PATCH 3/5] forceAttrs: make pos mandatory --- src/libcmd/installables.cc | 2 +- src/libexpr/eval-cache.cc | 2 +- src/libexpr/eval.cc | 2 +- src/libexpr/get-drvs.cc | 2 +- src/libexpr/primops.cc | 4 ++-- src/nix/flake.cc | 5 +++-- src/nix/prefetch.cc | 4 ++-- src/nix/repl.cc | 4 ++-- 8 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index 268861ce8..ec4f8f6f9 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -522,7 +522,7 @@ ref openEvalCache( auto vFlake = state.allocValue(); flake::callFlake(state, *lockedFlake, *vFlake); - state.forceAttrs(*vFlake); + state.forceAttrs(*vFlake, noPos); auto aOutputs = vFlake->attrs->get(state.symbols.create("outputs")); assert(aOutputs); 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) { diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 97f4d911c..486b43f0f 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -124,12 +124,13 @@ struct CmdFlakeLock : FlakeCommand static void enumerateOutputs(EvalState & state, Value & vFlake, std::function callback) { - state.forceAttrs(vFlake); + auto pos = vFlake.determinePos(noPos); + state.forceAttrs(vFlake, pos); auto aOutputs = vFlake.attrs->get(state.symbols.create("outputs")); assert(aOutputs); - state.forceAttrs(*aOutputs->value); + state.forceAttrs(*aOutputs->value, pos); auto sHydraJobs = state.symbols.create("hydraJobs"); diff --git a/src/nix/prefetch.cc b/src/nix/prefetch.cc index 768d37595..7044ead10 100644 --- a/src/nix/prefetch.cc +++ b/src/nix/prefetch.cc @@ -28,7 +28,7 @@ string resolveMirrorUrl(EvalState & state, string url) Value vMirrors; // FIXME: use nixpkgs flake state.eval(state.parseExprFromString("import ", "."), vMirrors); - state.forceAttrs(vMirrors); + state.forceAttrs(vMirrors, noPos); auto mirrorList = vMirrors.attrs->find(state.symbols.create(mirrorName)); if (mirrorList == vMirrors.attrs->end()) @@ -196,7 +196,7 @@ static int main_nix_prefetch_url(int argc, char * * argv) Value vRoot; state->evalFile(path, vRoot); Value & v(*findAlongAttrPath(*state, attrPath, autoArgs, vRoot).first); - state->forceAttrs(v); + state->forceAttrs(v, noPos); /* Extract the URL. */ auto & attr = v.attrs->need(state->symbols.create("urls")); diff --git a/src/nix/repl.cc b/src/nix/repl.cc index 5c2bf4bfd..8b6de1bf2 100644 --- a/src/nix/repl.cc +++ b/src/nix/repl.cc @@ -342,7 +342,7 @@ StringSet NixRepl::completePrefix(string prefix) Expr * e = parseString(expr); Value v; e->eval(*state, *env, v); - state->forceAttrs(v); + state->forceAttrs(v, noPos); for (auto & i : *v.attrs) { string name = i.name; @@ -673,7 +673,7 @@ void NixRepl::reloadFiles() void NixRepl::addAttrsToScope(Value & attrs) { - state->forceAttrs(attrs); + state->forceAttrs(attrs, attrs.determinePos(noPos)); if (displ + attrs.attrs->size() >= envSize) throw Error("environment full; cannot add more variables"); From 3d2ad2b70ba26114bfd5ba55d8b580d4f3b158af Mon Sep 17 00:00:00 2001 From: Kevin Amado Date: Fri, 21 Jan 2022 10:43:49 -0500 Subject: [PATCH 4/5] forceList: make pos mandatory --- src/nix/prefetch.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/nix/prefetch.cc b/src/nix/prefetch.cc index 7044ead10..de657d6f5 100644 --- a/src/nix/prefetch.cc +++ b/src/nix/prefetch.cc @@ -33,7 +33,7 @@ string resolveMirrorUrl(EvalState & state, string url) auto mirrorList = vMirrors.attrs->find(state.symbols.create(mirrorName)); if (mirrorList == vMirrors.attrs->end()) throw Error("unknown mirror name '%s'", mirrorName); - state.forceList(*mirrorList->value); + state.forceList(*mirrorList->value, noPos); if (mirrorList->value->listSize() < 1) throw Error("mirror URL '%s' did not expand to anything", url); @@ -200,7 +200,7 @@ static int main_nix_prefetch_url(int argc, char * * argv) /* Extract the URL. */ auto & attr = v.attrs->need(state->symbols.create("urls")); - state->forceList(*attr.value); + state->forceList(*attr.value, noPos); if (attr.value->listSize() < 1) throw Error("'urls' list is empty"); url = state->forceString(*attr.value->listElems()[0]); From 50efc5499a7d924828bed654be207a846c040fa0 Mon Sep 17 00:00:00 2001 From: Kevin Amado Date: Fri, 21 Jan 2022 11:44:19 -0500 Subject: [PATCH 5/5] 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(-) 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