diff --git a/src/libexpr/attr-set.hh b/src/libexpr/attr-set.hh index 6d68e5df3..1da8d91df 100644 --- a/src/libexpr/attr-set.hh +++ b/src/libexpr/attr-set.hh @@ -35,6 +35,7 @@ class Bindings { public: typedef uint32_t size_t; + Pos *pos; private: size_t size_, capacity_; diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 936bccc8c..ef9f8efca 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -201,6 +201,15 @@ string showType(const Value & v) } } +Pos Value::determinePos(const Pos &pos) const +{ + switch (internalType) { + case tAttrs: return *attrs->pos; + case tLambda: return lambda.fun->pos; + case tApp: return app.left->determinePos(pos); + default: return pos; + } +} bool Value::isTrivial() const { @@ -1060,6 +1069,8 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v) v.attrs->push_back(Attr(nameSym, i.valueExpr->maybeThunk(state, *dynamicEnv), &i.pos)); v.attrs->sort(); // FIXME: inefficient } + + v.attrs->pos = &pos; } diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index 8df8055b3..51a14cd59 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -180,6 +180,7 @@ struct ExprOpHasAttr : Expr struct ExprAttrs : Expr { bool recursive; + Pos pos; struct AttrDef { bool inherited; Expr * e; @@ -199,7 +200,8 @@ struct ExprAttrs : Expr }; typedef std::vector DynamicAttrDefs; DynamicAttrDefs dynamicAttrs; - ExprAttrs() : recursive(false) { }; + ExprAttrs(const Pos &pos) : recursive(false), pos(pos) { }; + ExprAttrs() : recursive(false), pos(noPos) { }; COMMON_METHODS }; diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 49d995bb9..f948dde47 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -478,7 +478,7 @@ binds $$->attrs[i.symbol] = ExprAttrs::AttrDef(new ExprSelect(CUR_POS, $4, i.symbol), makeCurPos(@6, data)); } } - | { $$ = new ExprAttrs; } + | { $$ = new ExprAttrs(makeCurPos(@0, data)); } ; attrs diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 428adf4c2..ff2f302ed 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -547,18 +547,56 @@ typedef list ValueList; #endif +static Bindings::iterator getAttr( + EvalState & state, + string funcName, + string attrName, + Bindings * attrSet, + const Pos & pos) +{ + Bindings::iterator value = attrSet->find(state.symbols.create(attrName)); + if (value == attrSet->end()) { + hintformat errorMsg = hintfmt( + "attribute '%s' missing for call to '%s'", + attrName, + funcName + ); + + Pos aPos = *attrSet->pos; + if (aPos == noPos) { + throw TypeError({ + .msg = errorMsg, + .errPos = pos, + }); + } else { + auto e = TypeError({ + .msg = errorMsg, + .errPos = aPos, + }); + + // 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 e; + } + } + + return value; +} + static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * args, Value & v) { state.forceAttrs(*args[0], pos); /* Get the start set. */ - Bindings::iterator startSet = - args[0]->attrs->find(state.symbols.create("startSet")); - if (startSet == args[0]->attrs->end()) - throw EvalError({ - .msg = hintfmt("attribute 'startSet' required"), - .errPos = pos - }); + Bindings::iterator startSet = getAttr( + state, + "genericClosure", + "startSet", + args[0]->attrs, + pos + ); + state.forceList(*startSet->value, pos); ValueList workSet; @@ -566,13 +604,14 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar workSet.push_back(startSet->value->listElems()[n]); /* Get the operator. */ - Bindings::iterator op = - args[0]->attrs->find(state.symbols.create("operator")); - if (op == args[0]->attrs->end()) - throw EvalError({ - .msg = hintfmt("attribute 'operator' required"), - .errPos = pos - }); + Bindings::iterator op = getAttr( + state, + "genericClosure", + "operator", + args[0]->attrs, + pos + ); + state.forceValue(*op->value, pos); /* Construct the closure by applying the operator to element of @@ -816,12 +855,14 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * state.forceAttrs(*args[0], pos); /* Figure out the name first (for stack backtraces). */ - Bindings::iterator attr = args[0]->attrs->find(state.sName); - if (attr == args[0]->attrs->end()) - throw EvalError({ - .msg = hintfmt("required attribute 'name' missing"), - .errPos = pos - }); + Bindings::iterator attr = getAttr( + state, + "derivationStrict", + state.sName, + args[0]->attrs, + pos + ); + string drvName; Pos & posDrvName(*attr->pos); try { @@ -953,7 +994,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * } } else { - auto s = state.coerceToString(posDrvName, *i->value, context, true); + 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; @@ -1369,12 +1410,13 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va if (i != v2.attrs->end()) prefix = state.forceStringNoCtx(*i->value, pos); - i = v2.attrs->find(state.symbols.create("path")); - if (i == v2.attrs->end()) - throw EvalError({ - .msg = hintfmt("attribute 'path' missing"), - .errPos = pos - }); + i = getAttr( + state, + "findFile", + "path", + v2.attrs, + pos + ); PathSet context; string path = state.coerceToString(pos, *i->value, context, false, false); @@ -2016,12 +2058,13 @@ 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 = args[1]->attrs->find(state.symbols.create(attr)); - if (i == args[1]->attrs->end()) - throw EvalError({ - .msg = hintfmt("attribute '%1%' missing", attr), - .errPos = pos - }); + Bindings::iterator i = getAttr( + state, + "getAttr", + attr, + args[1]->attrs, + pos + ); // !!! add to stack trace? if (state.countCalls && i->pos) state.attrSelects[*i->pos]++; state.forceValue(*i->value, pos); @@ -2148,22 +2191,25 @@ static void prim_listToAttrs(EvalState & state, const Pos & pos, Value * * args, Value & v2(*args[0]->listElems()[i]); state.forceAttrs(v2, pos); - Bindings::iterator j = v2.attrs->find(state.sName); - if (j == v2.attrs->end()) - throw TypeError({ - .msg = hintfmt("'name' attribute missing in a call to 'listToAttrs'"), - .errPos = pos - }); - string name = state.forceStringNoCtx(*j->value, pos); + Bindings::iterator j = getAttr( + state, + "listToAttrs", + state.sName, + v2.attrs, + pos + ); + + string name = state.forceStringNoCtx(*j->value, *j->pos); Symbol sym = state.symbols.create(name); if (seen.insert(sym).second) { - Bindings::iterator j2 = v2.attrs->find(state.symbols.create(state.sValue)); - if (j2 == v2.attrs->end()) - throw TypeError({ - .msg = hintfmt("'value' attribute missing in a call to 'listToAttrs'"), - .errPos = pos - }); + Bindings::iterator j2 = getAttr( + state, + "listToAttrs", + state.sValue, + v2.attrs, + pos + ); v.attrs->push_back(Attr(sym, j2->value, j2->pos)); } } @@ -2804,7 +2850,12 @@ static void prim_concatMap(EvalState & state, const Pos & pos, Value * * args, V 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); + try { + state.forceList(lists[n], lists[n].determinePos(args[0]->determinePos(pos))); + } catch (TypeError &e) { + e.addTrace(pos, hintfmt("while invoking '%s'", "concatMap")); + throw e; + } len += lists[n].listSize(); } diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh index b317c1898..a1f131f9e 100644 --- a/src/libexpr/value.hh +++ b/src/libexpr/value.hh @@ -341,6 +341,8 @@ public: return internalType == tList1 ? 1 : internalType == tList2 ? 2 : bigList.size; } + Pos determinePos(const Pos &pos) const; + /* Check whether forcing this value requires a trivial amount of computation. In particular, function applications are non-trivial. */