replace most Pos objects/ptrs with indexes into a position table

Pos objects are somewhat wasteful as they duplicate the origin file name and
input type for each object. on files that produce more than one Pos when parsed
this a sizeable waste of memory (one pointer per Pos). the same goes for
ptr<Pos> on 64 bit machines: parsing enough source to require 8 bytes to locate
a position would need at least 8GB of input and 64GB of expression memory. it's
not likely that we'll hit that any time soon, so we can use a uint32_t index to
locate positions instead.
This commit is contained in:
pennae 2022-03-04 19:31:59 +01:00
parent 34b72775cf
commit 6526d1676b
36 changed files with 752 additions and 622 deletions

View file

@ -473,7 +473,7 @@ struct InstallableAttrPath : InstallableValue
std::string what() const override { return attrPath; } std::string what() const override { return attrPath; }
std::pair<Value *, Pos> toValue(EvalState & state) override std::pair<Value *, PosIdx> toValue(EvalState & state) override
{ {
auto [vRes, pos] = findAlongAttrPath(state, attrPath, *cmd.getAutoArgs(state), **v); auto [vRes, pos] = findAlongAttrPath(state, attrPath, *cmd.getAutoArgs(state), **v);
state.forceValue(*vRes, pos); state.forceValue(*vRes, pos);
@ -613,7 +613,7 @@ std::vector<InstallableValue::DerivationInfo> InstallableFlake::toDerivations()
return res; return res;
} }
std::pair<Value *, Pos> InstallableFlake::toValue(EvalState & state) std::pair<Value *, PosIdx> InstallableFlake::toValue(EvalState & state)
{ {
return {&getCursor(state)->forceValue(), noPos}; return {&getCursor(state)->forceValue(), noPos};
} }

View file

@ -68,7 +68,7 @@ struct Installable
UnresolvedApp toApp(EvalState & state); UnresolvedApp toApp(EvalState & state);
virtual std::pair<Value *, Pos> toValue(EvalState & state) virtual std::pair<Value *, PosIdx> toValue(EvalState & state)
{ {
throw Error("argument '%s' cannot be evaluated", what()); throw Error("argument '%s' cannot be evaluated", what());
} }
@ -178,7 +178,7 @@ struct InstallableFlake : InstallableValue
std::vector<DerivationInfo> toDerivations() override; std::vector<DerivationInfo> toDerivations() override;
std::pair<Value *, Pos> toValue(EvalState & state) override; std::pair<Value *, PosIdx> toValue(EvalState & state) override;
/* Get a cursor to every attrpath in getActualAttrPaths() that /* Get a cursor to every attrpath in getActualAttrPaths() that
exists. */ exists. */

View file

@ -41,13 +41,13 @@ std::vector<Symbol> parseAttrPath(EvalState & state, std::string_view s)
} }
std::pair<Value *, Pos> findAlongAttrPath(EvalState & state, const std::string & attrPath, std::pair<Value *, PosIdx> findAlongAttrPath(EvalState & state, const std::string & attrPath,
Bindings & autoArgs, Value & vIn) Bindings & autoArgs, Value & vIn)
{ {
Strings tokens = parseAttrPath(attrPath); Strings tokens = parseAttrPath(attrPath);
Value * v = &vIn; Value * v = &vIn;
Pos pos = noPos; PosIdx pos = noPos;
for (auto & attr : tokens) { for (auto & attr : tokens) {
@ -83,7 +83,7 @@ std::pair<Value *, Pos> findAlongAttrPath(EvalState & state, const std::string &
throw AttrPathNotFound(suggestions, "attribute '%1%' in selection path '%2%' not found", attr, attrPath); throw AttrPathNotFound(suggestions, "attribute '%1%' in selection path '%2%' not found", attr, attrPath);
} }
v = &*a->value; v = &*a->value;
pos = *a->pos; pos = a->pos;
} }
else { else {

View file

@ -10,7 +10,7 @@ namespace nix {
MakeError(AttrPathNotFound, Error); MakeError(AttrPathNotFound, Error);
MakeError(NoPositionInfo, Error); MakeError(NoPositionInfo, Error);
std::pair<Value *, Pos> findAlongAttrPath( std::pair<Value *, PosIdx> findAlongAttrPath(
EvalState & state, EvalState & state,
const std::string & attrPath, const std::string & attrPath,
Bindings & autoArgs, Bindings & autoArgs,

View file

@ -40,7 +40,7 @@ Value * EvalState::allocAttr(Value & vAttrs, std::string_view name)
} }
Value & BindingsBuilder::alloc(const Symbol & name, ptr<Pos> pos) Value & BindingsBuilder::alloc(const Symbol & name, PosIdx pos)
{ {
auto value = state.allocValue(); auto value = state.allocValue();
bindings->push_back(Attr(name, value, pos)); bindings->push_back(Attr(name, value, pos));
@ -48,7 +48,7 @@ Value & BindingsBuilder::alloc(const Symbol & name, ptr<Pos> pos)
} }
Value & BindingsBuilder::alloc(std::string_view name, ptr<Pos> pos) Value & BindingsBuilder::alloc(std::string_view name, PosIdx pos)
{ {
return alloc(state.symbols.create(name), pos); return alloc(state.symbols.create(name), pos);
} }

View file

@ -17,10 +17,10 @@ struct Attr
{ {
Symbol name; Symbol name;
Value * value; Value * value;
ptr<Pos> pos; PosIdx pos;
Attr(Symbol name, Value * value, ptr<Pos> pos = ptr(&noPos)) Attr(Symbol name, Value * value, PosIdx pos = noPos)
: name(name), value(value), pos(pos) { }; : name(name), value(value), pos(pos) { };
Attr() : pos(&noPos) { }; Attr() { };
bool operator < (const Attr & a) const bool operator < (const Attr & a) const
{ {
return name < a.name; return name < a.name;
@ -35,13 +35,13 @@ class Bindings
{ {
public: public:
typedef uint32_t size_t; typedef uint32_t size_t;
ptr<Pos> pos; PosIdx pos;
private: private:
size_t size_, capacity_; size_t size_, capacity_;
Attr attrs[0]; Attr attrs[0];
Bindings(size_t capacity) : pos(&noPos), size_(0), capacity_(capacity) { } Bindings(size_t capacity) : size_(0), capacity_(capacity) { }
Bindings(const Bindings & bindings) = delete; Bindings(const Bindings & bindings) = delete;
public: public:
@ -118,7 +118,7 @@ public:
: bindings(bindings), state(state) : bindings(bindings), state(state)
{ } { }
void insert(Symbol name, Value * value, ptr<Pos> pos = ptr(&noPos)) void insert(Symbol name, Value * value, PosIdx pos = noPos)
{ {
insert(Attr(name, value, pos)); insert(Attr(name, value, pos));
} }
@ -133,9 +133,9 @@ public:
bindings->push_back(attr); bindings->push_back(attr);
} }
Value & alloc(const Symbol & name, ptr<Pos> pos = ptr(&noPos)); Value & alloc(const Symbol & name, PosIdx pos = noPos);
Value & alloc(std::string_view name, ptr<Pos> pos = ptr(&noPos)); Value & alloc(std::string_view name, PosIdx pos = noPos);
Bindings * finish() Bindings * finish()
{ {

View file

@ -80,7 +80,7 @@ Env & EvalState::allocEnv(size_t size)
[[gnu::always_inline]] [[gnu::always_inline]]
void EvalState::forceValue(Value & v, const Pos & pos) void EvalState::forceValue(Value & v, const PosIdx pos)
{ {
forceValue(v, [&]() { return pos; }); forceValue(v, [&]() { return pos; });
} }
@ -109,7 +109,7 @@ void EvalState::forceValue(Value & v, Callable getPos)
[[gnu::always_inline]] [[gnu::always_inline]]
inline void EvalState::forceAttrs(Value & v, const Pos & pos) inline void EvalState::forceAttrs(Value & v, const PosIdx pos)
{ {
forceAttrs(v, [&]() { return pos; }); forceAttrs(v, [&]() { return pos; });
} }
@ -126,7 +126,7 @@ inline void EvalState::forceAttrs(Value & v, Callable getPos)
[[gnu::always_inline]] [[gnu::always_inline]]
inline void EvalState::forceList(Value & v, const Pos & pos) inline void EvalState::forceList(Value & v, const PosIdx pos)
{ {
forceValue(v, pos); forceValue(v, pos);
if (!v.isList()) if (!v.isList())

View file

@ -236,10 +236,10 @@ std::string showType(const Value & v)
} }
} }
Pos Value::determinePos(const Pos & pos) const PosIdx Value::determinePos(const PosIdx pos) const
{ {
switch (internalType) { switch (internalType) {
case tAttrs: return *attrs->pos; case tAttrs: return attrs->pos;
case tLambda: return lambda.fun->pos; case tLambda: return lambda.fun->pos;
case tApp: return app.left->determinePos(pos); case tApp: return app.left->determinePos(pos);
default: return pos; default: return pos;
@ -698,7 +698,7 @@ std::optional<EvalState::Doc> EvalState::getDoc(Value & v)
auto v2 = &v; auto v2 = &v;
if (v2->primOp->doc) if (v2->primOp->doc)
return Doc { return Doc {
.pos = noPos, .pos = {},
.name = v2->primOp->name, .name = v2->primOp->name,
.arity = v2->primOp->arity, .arity = v2->primOp->arity,
.args = v2->primOp->args, .args = v2->primOp->args,
@ -714,21 +714,20 @@ std::optional<EvalState::Doc> EvalState::getDoc(Value & v)
evaluator. So here are some helper functions for throwing evaluator. So here are some helper functions for throwing
exceptions. */ exceptions. */
void EvalState::throwEvalError(const Pos & pos, const char * s) const void EvalState::throwEvalError(const PosIdx pos, const char * s) const
{ {
throw EvalError({ throw EvalError({
.msg = hintfmt(s), .msg = hintfmt(s),
.errPos = pos .errPos = positions[pos]
}); });
} }
void EvalState::throwTypeError(const Pos & pos, const char * s, const Value & v) const void EvalState::throwTypeError(const PosIdx pos, const char * s, const Value & v) const
{ {
throw TypeError({ throw TypeError({
.msg = hintfmt(s, showType(v)), .msg = hintfmt(s, showType(v)),
.errPos = pos .errPos = positions[pos]
}); });
} }
void EvalState::throwEvalError(const char * s, const std::string & s2) const void EvalState::throwEvalError(const char * s, const std::string & s2) const
@ -736,21 +735,21 @@ void EvalState::throwEvalError(const char * s, const std::string & s2) const
throw EvalError(s, s2); throw EvalError(s, s2);
} }
void EvalState::throwEvalError(const Pos & pos, const Suggestions & suggestions, const char * s, void EvalState::throwEvalError(const PosIdx pos, const Suggestions & suggestions, const char * s,
const std::string & s2) const const std::string & s2) const
{ {
throw EvalError(ErrorInfo { throw EvalError(ErrorInfo {
.msg = hintfmt(s, s2), .msg = hintfmt(s, s2),
.errPos = pos, .errPos = positions[pos],
.suggestions = suggestions, .suggestions = suggestions,
}); });
} }
void EvalState::throwEvalError(const Pos & pos, const char * s, const std::string & s2) const void EvalState::throwEvalError(const PosIdx pos, const char * s, const std::string & s2) const
{ {
throw EvalError(ErrorInfo { throw EvalError(ErrorInfo {
.msg = hintfmt(s, s2), .msg = hintfmt(s, s2),
.errPos = pos .errPos = positions[pos]
}); });
} }
@ -759,47 +758,47 @@ void EvalState::throwEvalError(const char * s, const std::string & s2, const std
throw EvalError(s, s2, s3); throw EvalError(s, s2, s3);
} }
void EvalState::throwEvalError(const Pos & pos, const char * s, const std::string & s2, void EvalState::throwEvalError(const PosIdx pos, const char * s, const std::string & s2,
const std::string & s3) const const std::string & s3) const
{ {
throw EvalError({ throw EvalError({
.msg = hintfmt(s, s2, s3), .msg = hintfmt(s, s2, s3),
.errPos = pos .errPos = positions[pos]
}); });
} }
void EvalState::throwEvalError(const Pos & p1, const char * s, const Symbol & sym, const Pos & p2) const void EvalState::throwEvalError(const PosIdx p1, const char * s, const Symbol & sym, const PosIdx p2) const
{ {
// p1 is where the error occurred; p2 is a position mentioned in the message. // p1 is where the error occurred; p2 is a position mentioned in the message.
throw EvalError({ throw EvalError({
.msg = hintfmt(s, sym, p2), .msg = hintfmt(s, sym, positions[p2]),
.errPos = p1 .errPos = positions[p1]
}); });
} }
void EvalState::throwTypeError(const Pos & pos, const char * s) const void EvalState::throwTypeError(const PosIdx pos, const char * s) const
{ {
throw TypeError({ throw TypeError({
.msg = hintfmt(s), .msg = hintfmt(s),
.errPos = pos .errPos = positions[pos]
}); });
} }
void EvalState::throwTypeError(const Pos & pos, const char * s, const ExprLambda & fun, void EvalState::throwTypeError(const PosIdx pos, const char * s, const ExprLambda & fun,
const Symbol & s2) const const Symbol & s2) const
{ {
throw TypeError({ throw TypeError({
.msg = hintfmt(s, fun.showNamePos(), s2), .msg = hintfmt(s, fun.showNamePos(positions), s2),
.errPos = pos .errPos = positions[pos]
}); });
} }
void EvalState::throwTypeError(const Pos & pos, const Suggestions & suggestions, const char * s, void EvalState::throwTypeError(const PosIdx pos, const Suggestions & suggestions, const char * s,
const ExprLambda & fun, const Symbol & s2) const const ExprLambda & fun, const Symbol & s2) const
{ {
throw TypeError(ErrorInfo { throw TypeError(ErrorInfo {
.msg = hintfmt(s, fun.showNamePos(), s2), .msg = hintfmt(s, fun.showNamePos(positions), s2),
.errPos = pos, .errPos = positions[pos],
.suggestions = suggestions, .suggestions = suggestions,
}); });
} }
@ -810,27 +809,27 @@ void EvalState::throwTypeError(const char * s, const Value & v) const
throw TypeError(s, showType(v)); throw TypeError(s, showType(v));
} }
void EvalState::throwAssertionError(const Pos & pos, const char * s, const std::string & s1) const void EvalState::throwAssertionError(const PosIdx pos, const char * s, const std::string & s1) const
{ {
throw AssertionError({ throw AssertionError({
.msg = hintfmt(s, s1), .msg = hintfmt(s, s1),
.errPos = pos .errPos = positions[pos]
}); });
} }
void EvalState::throwUndefinedVarError(const Pos & pos, const char * s, const std::string & s1) const void EvalState::throwUndefinedVarError(const PosIdx pos, const char * s, const std::string & s1) const
{ {
throw UndefinedVarError({ throw UndefinedVarError({
.msg = hintfmt(s, s1), .msg = hintfmt(s, s1),
.errPos = pos .errPos = positions[pos]
}); });
} }
void EvalState::throwMissingArgumentError(const Pos & pos, const char * s, const std::string & s1) const void EvalState::throwMissingArgumentError(const PosIdx pos, const char * s, const std::string & s1) const
{ {
throw MissingArgumentError({ throw MissingArgumentError({
.msg = hintfmt(s, s1), .msg = hintfmt(s, s1),
.errPos = pos .errPos = positions[pos]
}); });
} }
@ -839,9 +838,9 @@ void EvalState::addErrorTrace(Error & e, const char * s, const std::string & s2)
e.addTrace(std::nullopt, s, s2); e.addTrace(std::nullopt, s, s2);
} }
void EvalState::addErrorTrace(Error & e, const Pos & pos, const char * s, const std::string & s2) const void EvalState::addErrorTrace(Error & e, const PosIdx pos, const char * s, const std::string & s2) const
{ {
e.addTrace(pos, s, s2); e.addTrace(positions[pos], s, s2);
} }
@ -898,7 +897,7 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval)
} }
Bindings::iterator j = env->values[0]->attrs->find(var.name); Bindings::iterator j = env->values[0]->attrs->find(var.name);
if (j != env->values[0]->attrs->end()) { if (j != env->values[0]->attrs->end()) {
if (countCalls) attrSelects[*j->pos]++; if (countCalls) attrSelects[j->pos]++;
return j->value; return j->value;
} }
if (!env->prevWith) if (!env->prevWith)
@ -932,13 +931,14 @@ void EvalState::mkThunk_(Value & v, Expr * expr)
} }
void EvalState::mkPos(Value & v, ptr<Pos> pos) void EvalState::mkPos(Value & v, PosIdx p)
{ {
if (pos->file.set()) { auto pos = positions[p];
if (pos.file.set()) {
auto attrs = buildBindings(3); auto attrs = buildBindings(3);
attrs.alloc(sFile).mkString(pos->file); attrs.alloc(sFile).mkString(pos.file);
attrs.alloc(sLine).mkInt(pos->line); attrs.alloc(sLine).mkInt(pos.line);
attrs.alloc(sColumn).mkInt(pos->column); attrs.alloc(sColumn).mkInt(pos.column);
v.mkAttrs(attrs); v.mkAttrs(attrs);
} else } else
v.mkNull(); v.mkNull();
@ -1071,7 +1071,7 @@ inline bool EvalState::evalBool(Env & env, Expr * e)
} }
inline bool EvalState::evalBool(Env & env, Expr * e, const Pos & pos) inline bool EvalState::evalBool(Env & env, Expr * e, const PosIdx pos)
{ {
Value v; Value v;
e->eval(*this, env, v); e->eval(*this, env, v);
@ -1145,7 +1145,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
} else } else
vAttr = i.second.e->maybeThunk(state, i.second.inherited ? env : env2); 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, ptr(&i.second.pos))); v.attrs->push_back(Attr(i.first, vAttr, i.second.pos));
} }
/* If the rec contains an attribute called `__overrides', then /* If the rec contains an attribute called `__overrides', then
@ -1177,7 +1177,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
else else
for (auto & i : attrs) for (auto & i : attrs)
v.attrs->push_back(Attr(i.first, i.second.e->maybeThunk(state, env), ptr(&i.second.pos))); v.attrs->push_back(Attr(i.first, i.second.e->maybeThunk(state, env), i.second.pos));
/* Dynamic attrs apply *after* rec and __overrides. */ /* Dynamic attrs apply *after* rec and __overrides. */
for (auto & i : dynamicAttrs) { for (auto & i : dynamicAttrs) {
@ -1190,15 +1190,15 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
Symbol nameSym = state.symbols.create(nameVal.string.s); Symbol nameSym = state.symbols.create(nameVal.string.s);
Bindings::iterator j = v.attrs->find(nameSym); Bindings::iterator j = v.attrs->find(nameSym);
if (j != v.attrs->end()) if (j != v.attrs->end())
state.throwEvalError(i.pos, "dynamic attribute '%1%' already defined at %2%", nameSym, *j->pos); state.throwEvalError(i.pos, "dynamic attribute '%1%' already defined at %2%", nameSym, j->pos);
i.valueExpr->setName(nameSym); i.valueExpr->setName(nameSym);
/* Keep sorted order so find can catch duplicates */ /* Keep sorted order so find can catch duplicates */
v.attrs->push_back(Attr(nameSym, i.valueExpr->maybeThunk(state, *dynamicEnv), ptr(&i.pos))); v.attrs->push_back(Attr(nameSym, i.valueExpr->maybeThunk(state, *dynamicEnv), i.pos));
v.attrs->sort(); // FIXME: inefficient v.attrs->sort(); // FIXME: inefficient
} }
v.attrs->pos = ptr(&pos); v.attrs->pos = pos;
} }
@ -1256,7 +1256,7 @@ static std::string showAttrPath(EvalState & state, Env & env, const AttrPath & a
void ExprSelect::eval(EvalState & state, Env & env, Value & v) void ExprSelect::eval(EvalState & state, Env & env, Value & v)
{ {
Value vTmp; Value vTmp;
ptr<Pos> pos2(&noPos); PosIdx pos2;
Value * vAttrs = &vTmp; Value * vAttrs = &vTmp;
e->eval(state, env, vTmp); e->eval(state, env, vTmp);
@ -1289,14 +1289,15 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
} }
vAttrs = j->value; vAttrs = j->value;
pos2 = j->pos; pos2 = j->pos;
if (state.countCalls) state.attrSelects[*pos2]++; if (state.countCalls) state.attrSelects[pos2]++;
} }
state.forceValue(*vAttrs, (*pos2 != noPos ? *pos2 : this->pos ) ); state.forceValue(*vAttrs, (pos2 ? pos2 : this->pos ) );
} catch (Error & e) { } catch (Error & e) {
if (*pos2 != noPos && pos2->file != state.sDerivationNix) auto pos2r = state.positions[pos2];
state.addErrorTrace(e, *pos2, "while evaluating the attribute '%1%'", if (pos2 && pos2r.file != state.sDerivationNix)
state.addErrorTrace(e, pos2, "while evaluating the attribute '%1%'",
showAttrPath(state, env, attrPath)); showAttrPath(state, env, attrPath));
throw; throw;
} }
@ -1336,9 +1337,11 @@ void ExprLambda::eval(EvalState & state, Env & env, Value & v)
} }
void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & vRes, const Pos & pos) void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & vRes, const PosIdx pos)
{ {
auto trace = evalSettings.traceFunctionCalls ? std::make_unique<FunctionCallTrace>(pos) : nullptr; auto trace = evalSettings.traceFunctionCalls
? std::make_unique<FunctionCallTrace>(positions[pos])
: nullptr;
forceValue(fun, pos); forceValue(fun, pos);
@ -1701,7 +1704,7 @@ void ExprOpConcatLists::eval(EvalState & state, Env & env, Value & v)
} }
void EvalState::concatLists(Value & v, size_t nrLists, Value * * lists, const Pos & pos) void EvalState::concatLists(Value & v, size_t nrLists, Value * * lists, const PosIdx pos)
{ {
nrListConcats++; nrListConcats++;
@ -1821,7 +1824,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
void ExprPos::eval(EvalState & state, Env & env, Value & v) void ExprPos::eval(EvalState & state, Env & env, Value & v)
{ {
state.mkPos(v, ptr(&pos)); state.mkPos(v, pos);
} }
@ -1841,7 +1844,7 @@ void EvalState::forceValueDeep(Value & v)
try { try {
recurse(*i.value); recurse(*i.value);
} catch (Error & e) { } catch (Error & e) {
addErrorTrace(e, *i.pos, "while evaluating the attribute '%1%'", i.name); addErrorTrace(e, i.pos, "while evaluating the attribute '%1%'", i.name);
throw; throw;
} }
} }
@ -1856,7 +1859,7 @@ void EvalState::forceValueDeep(Value & v)
} }
NixInt EvalState::forceInt(Value & v, const Pos & pos) NixInt EvalState::forceInt(Value & v, const PosIdx pos)
{ {
forceValue(v, pos); forceValue(v, pos);
if (v.type() != nInt) if (v.type() != nInt)
@ -1865,7 +1868,7 @@ NixInt EvalState::forceInt(Value & v, const Pos & pos)
} }
NixFloat EvalState::forceFloat(Value & v, const Pos & pos) NixFloat EvalState::forceFloat(Value & v, const PosIdx pos)
{ {
forceValue(v, pos); forceValue(v, pos);
if (v.type() == nInt) if (v.type() == nInt)
@ -1876,7 +1879,7 @@ NixFloat EvalState::forceFloat(Value & v, const Pos & pos)
} }
bool EvalState::forceBool(Value & v, const Pos & pos) bool EvalState::forceBool(Value & v, const PosIdx pos)
{ {
forceValue(v, pos); forceValue(v, pos);
if (v.type() != nBool) if (v.type() != nBool)
@ -1891,7 +1894,7 @@ bool EvalState::isFunctor(Value & fun)
} }
void EvalState::forceFunction(Value & v, const Pos & pos) void EvalState::forceFunction(Value & v, const PosIdx pos)
{ {
forceValue(v, pos); forceValue(v, pos);
if (v.type() != nFunction && !isFunctor(v)) if (v.type() != nFunction && !isFunctor(v))
@ -1899,7 +1902,7 @@ void EvalState::forceFunction(Value & v, const Pos & pos)
} }
std::string_view EvalState::forceString(Value & v, const Pos & pos) std::string_view EvalState::forceString(Value & v, const PosIdx pos)
{ {
forceValue(v, pos); forceValue(v, pos);
if (v.type() != nString) { if (v.type() != nString) {
@ -1952,7 +1955,7 @@ NixStringContext Value::getContext(const Store & store)
} }
std::string_view EvalState::forceString(Value & v, PathSet & context, const Pos & pos) std::string_view EvalState::forceString(Value & v, PathSet & context, const PosIdx pos)
{ {
auto s = forceString(v, pos); auto s = forceString(v, pos);
copyContext(v, context); copyContext(v, context);
@ -1960,7 +1963,7 @@ std::string_view EvalState::forceString(Value & v, PathSet & context, const Pos
} }
std::string_view EvalState::forceStringNoCtx(Value & v, const Pos & pos) std::string_view EvalState::forceStringNoCtx(Value & v, const PosIdx pos)
{ {
auto s = forceString(v, pos); auto s = forceString(v, pos);
if (v.string.context) { if (v.string.context) {
@ -1980,13 +1983,13 @@ bool EvalState::isDerivation(Value & v)
if (v.type() != nAttrs) return false; if (v.type() != nAttrs) return false;
Bindings::iterator i = v.attrs->find(sType); Bindings::iterator i = v.attrs->find(sType);
if (i == v.attrs->end()) return false; if (i == v.attrs->end()) return false;
forceValue(*i->value, *i->pos); forceValue(*i->value, i->pos);
if (i->value->type() != nString) return false; if (i->value->type() != nString) return false;
return strcmp(i->value->string.s, "derivation") == 0; return strcmp(i->value->string.s, "derivation") == 0;
} }
std::optional<std::string> EvalState::tryAttrsToString(const Pos & pos, Value & v, std::optional<std::string> EvalState::tryAttrsToString(const PosIdx pos, Value & v,
PathSet & context, bool coerceMore, bool copyToStore) PathSet & context, bool coerceMore, bool copyToStore)
{ {
auto i = v.attrs->find(sToString); auto i = v.attrs->find(sToString);
@ -1999,7 +2002,7 @@ std::optional<std::string> EvalState::tryAttrsToString(const Pos & pos, Value &
return {}; return {};
} }
BackedStringView EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context, BackedStringView EvalState::coerceToString(const PosIdx pos, Value & v, PathSet & context,
bool coerceMore, bool copyToStore, bool canonicalizePath) bool coerceMore, bool copyToStore, bool canonicalizePath)
{ {
forceValue(v, pos); forceValue(v, pos);
@ -2028,7 +2031,7 @@ BackedStringView EvalState::coerceToString(const Pos & pos, Value & v, PathSet &
} }
if (v.type() == nExternal) if (v.type() == nExternal)
return v.external->coerceToString(pos, context, coerceMore, copyToStore); return v.external->coerceToString(positions[pos], context, coerceMore, copyToStore);
if (coerceMore) { if (coerceMore) {
@ -2081,7 +2084,7 @@ std::string EvalState::copyPathToStore(PathSet & context, const Path & path)
} }
Path EvalState::coerceToPath(const Pos & pos, Value & v, PathSet & context) Path EvalState::coerceToPath(const PosIdx pos, Value & v, PathSet & context)
{ {
auto path = coerceToString(pos, v, context, false, false).toOwned(); auto path = coerceToString(pos, v, context, false, false).toOwned();
if (path == "" || path[0] != '/') if (path == "" || path[0] != '/')
@ -2090,14 +2093,14 @@ Path EvalState::coerceToPath(const Pos & pos, Value & v, PathSet & context)
} }
StorePath EvalState::coerceToStorePath(const Pos & pos, Value & v, PathSet & context) StorePath EvalState::coerceToStorePath(const PosIdx pos, Value & v, PathSet & context)
{ {
auto path = coerceToString(pos, v, context, false, false).toOwned(); auto path = coerceToString(pos, v, context, false, false).toOwned();
if (auto storePath = store->maybeParseStorePath(path)) if (auto storePath = store->maybeParseStorePath(path))
return *storePath; return *storePath;
throw EvalError({ throw EvalError({
.msg = hintfmt("path '%1%' is not in the Nix store", path), .msg = hintfmt("path '%1%' is not in the Nix store", path),
.errPos = pos .errPos = positions[pos]
}); });
} }
@ -2268,10 +2271,10 @@ void EvalState::printStats()
obj.attr("name", (const std::string &) i.first->name); obj.attr("name", (const std::string &) i.first->name);
else else
obj.attr("name", nullptr); obj.attr("name", nullptr);
if (i.first->pos) { if (auto pos = positions[i.first->pos]) {
obj.attr("file", (const std::string &) i.first->pos.file); obj.attr("file", (const std::string &) pos.file);
obj.attr("line", i.first->pos.line); obj.attr("line", pos.line);
obj.attr("column", i.first->pos.column); obj.attr("column", pos.column);
} }
obj.attr("count", i.second); obj.attr("count", i.second);
} }
@ -2280,10 +2283,10 @@ void EvalState::printStats()
auto list = topObj.list("attributes"); auto list = topObj.list("attributes");
for (auto & i : attrSelects) { for (auto & i : attrSelects) {
auto obj = list.object(); auto obj = list.object();
if (i.first) { if (auto pos = positions[i.first]) {
obj.attr("file", (const std::string &) i.first.file); obj.attr("file", (const std::string &) pos.file);
obj.attr("line", i.first.line); obj.attr("line", pos.line);
obj.attr("column", i.first.column); obj.attr("column", pos.column);
} }
obj.attr("count", i.second); obj.attr("count", i.second);
} }

View file

@ -23,7 +23,7 @@ class StorePath;
enum RepairFlag : bool; enum RepairFlag : bool;
typedef void (* PrimOpFun) (EvalState & state, const Pos & pos, Value * * args, Value & v); typedef void (* PrimOpFun) (EvalState & state, const PosIdx pos, Value * * args, Value & v);
struct PrimOp struct PrimOp
@ -73,6 +73,7 @@ class EvalState
{ {
public: public:
SymbolTable symbols; SymbolTable symbols;
PosTable positions;
const Symbol sWith, sOutPath, sDrvPath, sType, sMeta, sName, sValue, const Symbol sWith, sOutPath, sDrvPath, sType, sMeta, sName, sValue,
sSystem, sOverrides, sOutputs, sOutputName, sIgnoreNulls, sSystem, sOverrides, sOutputs, sOutputName, sIgnoreNulls,
@ -205,7 +206,7 @@ public:
/* Look up a file in the search path. */ /* Look up a file in the search path. */
Path findFile(const std::string_view path); Path findFile(const std::string_view path);
Path findFile(SearchPath & searchPath, const std::string_view path, const Pos & pos = noPos); Path findFile(SearchPath & searchPath, const std::string_view path, const PosIdx pos = noPos);
/* If the specified search path element is a URI, download it. */ /* If the specified search path element is a URI, download it. */
std::pair<bool, std::string> resolveSearchPathElem(const SearchPathElem & elem); std::pair<bool, std::string> resolveSearchPathElem(const SearchPathElem & elem);
@ -217,14 +218,14 @@ public:
/* Evaluation the expression, then verify that it has the expected /* Evaluation the expression, then verify that it has the expected
type. */ type. */
inline bool evalBool(Env & env, Expr * e); inline bool evalBool(Env & env, Expr * e);
inline bool evalBool(Env & env, Expr * e, const Pos & pos); inline bool evalBool(Env & env, Expr * e, const PosIdx pos);
inline void evalAttrs(Env & env, Expr * e, Value & v); inline void evalAttrs(Env & env, Expr * e, Value & v);
/* If `v' is a thunk, enter it and overwrite `v' with the result /* If `v' is a thunk, enter it and overwrite `v' with the result
of the evaluation of the thunk. If `v' is a delayed function of the evaluation of the thunk. If `v' is a delayed function
application, call the function and overwrite `v' with the application, call the function and overwrite `v' with the
result. Otherwise, this is a no-op. */ result. Otherwise, this is a no-op. */
inline void forceValue(Value & v, const Pos & pos); inline void forceValue(Value & v, const PosIdx pos);
template <typename Callable> template <typename Callable>
inline void forceValue(Value & v, Callable getPos); inline void forceValue(Value & v, Callable getPos);
@ -234,72 +235,72 @@ public:
void forceValueDeep(Value & v); void forceValueDeep(Value & v);
/* Force `v', and then verify that it has the expected type. */ /* Force `v', and then verify that it has the expected type. */
NixInt forceInt(Value & v, const Pos & pos); NixInt forceInt(Value & v, const PosIdx pos);
NixFloat forceFloat(Value & v, const Pos & pos); NixFloat forceFloat(Value & v, const PosIdx pos);
bool forceBool(Value & v, const Pos & pos); bool forceBool(Value & v, const PosIdx pos);
void forceAttrs(Value & v, const Pos & pos); void forceAttrs(Value & v, const PosIdx pos);
template <typename Callable> template <typename Callable>
inline void forceAttrs(Value & v, Callable getPos); inline void forceAttrs(Value & v, Callable getPos);
inline void forceList(Value & v, const Pos & pos); inline void forceList(Value & v, const PosIdx pos);
void forceFunction(Value & v, const Pos & pos); // either lambda or primop void forceFunction(Value & v, const PosIdx pos); // either lambda or primop
std::string_view forceString(Value & v, const Pos & pos = noPos); std::string_view forceString(Value & v, const PosIdx pos = noPos);
std::string_view forceString(Value & v, PathSet & context, const Pos & pos = noPos); std::string_view forceString(Value & v, PathSet & context, const PosIdx pos = noPos);
std::string_view forceStringNoCtx(Value & v, const Pos & pos = noPos); std::string_view forceStringNoCtx(Value & v, const PosIdx pos = noPos);
[[gnu::noinline, gnu::noreturn]] [[gnu::noinline, gnu::noreturn]]
void throwEvalError(const Pos & pos, const char * s) const; void throwEvalError(const PosIdx pos, const char * s) const;
[[gnu::noinline, gnu::noreturn]] [[gnu::noinline, gnu::noreturn]]
void throwTypeError(const Pos & pos, const char * s, const Value & v) const; void throwTypeError(const PosIdx pos, const char * s, const Value & v) const;
[[gnu::noinline, gnu::noreturn]] [[gnu::noinline, gnu::noreturn]]
void throwEvalError(const char * s, const std::string & s2) const; void throwEvalError(const char * s, const std::string & s2) const;
[[gnu::noinline, gnu::noreturn]] [[gnu::noinline, gnu::noreturn]]
void throwEvalError(const Pos & pos, const Suggestions & suggestions, const char * s, void throwEvalError(const PosIdx pos, const Suggestions & suggestions, const char * s,
const std::string & s2) const; const std::string & s2) const;
[[gnu::noinline, gnu::noreturn]] [[gnu::noinline, gnu::noreturn]]
void throwEvalError(const Pos & pos, const char * s, const std::string & s2) const; void throwEvalError(const PosIdx pos, const char * s, const std::string & s2) const;
[[gnu::noinline, gnu::noreturn]] [[gnu::noinline, gnu::noreturn]]
void throwEvalError(const char * s, const std::string & s2, const std::string & s3) const; void throwEvalError(const char * s, const std::string & s2, const std::string & s3) const;
[[gnu::noinline, gnu::noreturn]] [[gnu::noinline, gnu::noreturn]]
void throwEvalError(const Pos & pos, const char * s, const std::string & s2, const std::string & s3) const; void throwEvalError(const PosIdx pos, const char * s, const std::string & s2, const std::string & s3) const;
[[gnu::noinline, gnu::noreturn]] [[gnu::noinline, gnu::noreturn]]
void throwEvalError(const Pos & p1, const char * s, const Symbol & sym, const Pos & p2) const; void throwEvalError(const PosIdx p1, const char * s, const Symbol & sym, const PosIdx p2) const;
[[gnu::noinline, gnu::noreturn]] [[gnu::noinline, gnu::noreturn]]
void throwTypeError(const Pos & pos, const char * s) const; void throwTypeError(const PosIdx pos, const char * s) const;
[[gnu::noinline, gnu::noreturn]] [[gnu::noinline, gnu::noreturn]]
void throwTypeError(const Pos & pos, const char * s, const ExprLambda & fun, const Symbol & s2) const; void throwTypeError(const PosIdx pos, const char * s, const ExprLambda & fun, const Symbol & s2) const;
[[gnu::noinline, gnu::noreturn]] [[gnu::noinline, gnu::noreturn]]
void throwTypeError(const Pos & pos, const Suggestions & suggestions, const char * s, void throwTypeError(const PosIdx pos, const Suggestions & suggestions, const char * s,
const ExprLambda & fun, const Symbol & s2) const; const ExprLambda & fun, const Symbol & s2) const;
[[gnu::noinline, gnu::noreturn]] [[gnu::noinline, gnu::noreturn]]
void throwTypeError(const char * s, const Value & v) const; void throwTypeError(const char * s, const Value & v) const;
[[gnu::noinline, gnu::noreturn]] [[gnu::noinline, gnu::noreturn]]
void throwAssertionError(const Pos & pos, const char * s, const std::string & s1) const; void throwAssertionError(const PosIdx pos, const char * s, const std::string & s1) const;
[[gnu::noinline, gnu::noreturn]] [[gnu::noinline, gnu::noreturn]]
void throwUndefinedVarError(const Pos & pos, const char * s, const std::string & s1) const; void throwUndefinedVarError(const PosIdx pos, const char * s, const std::string & s1) const;
[[gnu::noinline, gnu::noreturn]] [[gnu::noinline, gnu::noreturn]]
void throwMissingArgumentError(const Pos & pos, const char * s, const std::string & s1) const; void throwMissingArgumentError(const PosIdx pos, const char * s, const std::string & s1) const;
[[gnu::noinline]] [[gnu::noinline]]
void addErrorTrace(Error & e, const char * s, const std::string & s2) const; void addErrorTrace(Error & e, const char * s, const std::string & s2) const;
[[gnu::noinline]] [[gnu::noinline]]
void addErrorTrace(Error & e, const Pos & pos, const char * s, const std::string & s2) const; void addErrorTrace(Error & e, const PosIdx pos, const char * s, const std::string & s2) const;
public: public:
/* Return true iff the value `v' denotes a derivation (i.e. a /* Return true iff the value `v' denotes a derivation (i.e. a
set with attribute `type = "derivation"'). */ set with attribute `type = "derivation"'). */
bool isDerivation(Value & v); bool isDerivation(Value & v);
std::optional<std::string> tryAttrsToString(const Pos & pos, Value & v, std::optional<std::string> tryAttrsToString(const PosIdx pos, Value & v,
PathSet & context, bool coerceMore = false, bool copyToStore = true); PathSet & context, bool coerceMore = false, bool copyToStore = true);
/* String coercion. Converts strings, paths and derivations to a /* String coercion. Converts strings, paths and derivations to a
string. If `coerceMore' is set, also converts nulls, integers, string. If `coerceMore' is set, also converts nulls, integers,
booleans and lists to a string. If `copyToStore' is set, booleans and lists to a string. If `copyToStore' is set,
referenced paths are copied to the Nix store as a side effect. */ referenced paths are copied to the Nix store as a side effect. */
BackedStringView coerceToString(const Pos & pos, Value & v, PathSet & context, BackedStringView coerceToString(const PosIdx pos, Value & v, PathSet & context,
bool coerceMore = false, bool copyToStore = true, bool coerceMore = false, bool copyToStore = true,
bool canonicalizePath = true); bool canonicalizePath = true);
@ -308,10 +309,10 @@ public:
/* Path coercion. Converts strings, paths and derivations to a /* Path coercion. Converts strings, paths and derivations to a
path. The result is guaranteed to be a canonicalised, absolute path. The result is guaranteed to be a canonicalised, absolute
path. Nothing is copied to the store. */ path. Nothing is copied to the store. */
Path coerceToPath(const Pos & pos, Value & v, PathSet & context); Path coerceToPath(const PosIdx pos, Value & v, PathSet & context);
/* Like coerceToPath, but the result must be a store path. */ /* Like coerceToPath, but the result must be a store path. */
StorePath coerceToStorePath(const Pos & pos, Value & v, PathSet & context); StorePath coerceToStorePath(const PosIdx pos, Value & v, PathSet & context);
public: public:
@ -372,9 +373,9 @@ public:
bool isFunctor(Value & fun); bool isFunctor(Value & fun);
// FIXME: use std::span // FIXME: use std::span
void callFunction(Value & fun, size_t nrArgs, Value * * args, Value & vRes, const Pos & pos); void callFunction(Value & fun, size_t nrArgs, Value * * args, Value & vRes, const PosIdx pos);
void callFunction(Value & fun, Value & arg, Value & vRes, const Pos & pos) void callFunction(Value & fun, Value & arg, Value & vRes, const PosIdx pos)
{ {
Value * args[] = {&arg}; Value * args[] = {&arg};
callFunction(fun, 1, args, vRes, pos); callFunction(fun, 1, args, vRes, pos);
@ -400,9 +401,9 @@ public:
void mkList(Value & v, size_t length); void mkList(Value & v, size_t length);
void mkThunk_(Value & v, Expr * expr); void mkThunk_(Value & v, Expr * expr);
void mkPos(Value & v, ptr<Pos> pos); void mkPos(Value & v, PosIdx pos);
void concatLists(Value & v, size_t nrLists, Value * * lists, const Pos & pos); void concatLists(Value & v, size_t nrLists, Value * * lists, const PosIdx pos);
/* Print statistics. */ /* Print statistics. */
void printStats(); void printStats();
@ -438,7 +439,7 @@ private:
void incrFunctionCall(ExprLambda * fun); void incrFunctionCall(ExprLambda * fun);
typedef std::map<Pos, size_t> AttrSelects; typedef std::map<PosIdx, size_t> AttrSelects;
AttrSelects attrSelects; AttrSelects attrSelects;
friend struct ExprOpUpdate; friend struct ExprOpUpdate;
@ -449,9 +450,9 @@ private:
friend struct ExprFloat; friend struct ExprFloat;
friend struct ExprPath; friend struct ExprPath;
friend struct ExprSelect; friend struct ExprSelect;
friend void prim_getAttr(EvalState & state, const Pos & pos, Value * * args, Value & v); friend void prim_getAttr(EvalState & state, const PosIdx pos, Value * * args, Value & v);
friend void prim_match(EvalState & state, const Pos & pos, Value * * args, Value & v); friend void prim_match(EvalState & state, const PosIdx pos, Value * * args, Value & v);
friend void prim_split(EvalState & state, const Pos & pos, Value * * args, Value & v); friend void prim_split(EvalState & state, const PosIdx pos, Value * * args, Value & v);
friend struct Value; friend struct Value;
}; };

View file

@ -72,7 +72,7 @@ static std::tuple<fetchers::Tree, FlakeRef, FlakeRef> fetchOrSubstituteTree(
return {std::move(tree), resolvedRef, lockedRef}; return {std::move(tree), resolvedRef, lockedRef};
} }
static void forceTrivialValue(EvalState & state, Value & value, const Pos & pos) static void forceTrivialValue(EvalState & state, Value & value, const PosIdx pos)
{ {
if (value.isThunk() && value.isTrivial()) if (value.isThunk() && value.isTrivial())
state.forceValue(value, pos); state.forceValue(value, pos);
@ -80,20 +80,20 @@ static void forceTrivialValue(EvalState & state, Value & value, const Pos & pos)
static void expectType(EvalState & state, ValueType type, static void expectType(EvalState & state, ValueType type,
Value & value, const Pos & pos) Value & value, const PosIdx pos)
{ {
forceTrivialValue(state, value, pos); forceTrivialValue(state, value, pos);
if (value.type() != type) if (value.type() != type)
throw Error("expected %s but got %s at %s", throw Error("expected %s but got %s at %s",
showType(type), showType(value.type()), pos); showType(type), showType(value.type()), state.positions[pos]);
} }
static std::map<FlakeId, FlakeInput> parseFlakeInputs( static std::map<FlakeId, FlakeInput> parseFlakeInputs(
EvalState & state, Value * value, const Pos & pos, EvalState & state, Value * value, const PosIdx pos,
const std::optional<Path> & baseDir, InputPath lockRootPath); const std::optional<Path> & baseDir, InputPath lockRootPath);
static FlakeInput parseFlakeInput(EvalState & state, static FlakeInput parseFlakeInput(EvalState & state,
const std::string & inputName, Value * value, const Pos & pos, const std::string & inputName, Value * value, const PosIdx pos,
const std::optional<Path> & baseDir, InputPath lockRootPath) const std::optional<Path> & baseDir, InputPath lockRootPath)
{ {
expectType(state, nAttrs, *value, pos); expectType(state, nAttrs, *value, pos);
@ -111,16 +111,16 @@ static FlakeInput parseFlakeInput(EvalState & state,
for (nix::Attr attr : *(value->attrs)) { for (nix::Attr attr : *(value->attrs)) {
try { try {
if (attr.name == sUrl) { if (attr.name == sUrl) {
expectType(state, nString, *attr.value, *attr.pos); expectType(state, nString, *attr.value, attr.pos);
url = attr.value->string.s; url = attr.value->string.s;
attrs.emplace("url", *url); attrs.emplace("url", *url);
} else if (attr.name == sFlake) { } else if (attr.name == sFlake) {
expectType(state, nBool, *attr.value, *attr.pos); expectType(state, nBool, *attr.value, attr.pos);
input.isFlake = attr.value->boolean; input.isFlake = attr.value->boolean;
} else if (attr.name == sInputs) { } else if (attr.name == sInputs) {
input.overrides = parseFlakeInputs(state, attr.value, *attr.pos, baseDir, lockRootPath); input.overrides = parseFlakeInputs(state, attr.value, attr.pos, baseDir, lockRootPath);
} else if (attr.name == sFollows) { } else if (attr.name == sFollows) {
expectType(state, nString, *attr.value, *attr.pos); expectType(state, nString, *attr.value, attr.pos);
auto follows(parseInputPath(attr.value->string.s)); auto follows(parseInputPath(attr.value->string.s));
follows.insert(follows.begin(), lockRootPath.begin(), lockRootPath.end()); follows.insert(follows.begin(), lockRootPath.begin(), lockRootPath.end());
input.follows = follows; input.follows = follows;
@ -141,7 +141,7 @@ static FlakeInput parseFlakeInput(EvalState & state,
} }
} }
} catch (Error & e) { } catch (Error & e) {
e.addTrace(*attr.pos, hintfmt("in flake attribute '%s'", attr.name)); e.addTrace(state.positions[attr.pos], hintfmt("in flake attribute '%s'", attr.name));
throw; throw;
} }
} }
@ -150,13 +150,13 @@ static FlakeInput parseFlakeInput(EvalState & state,
try { try {
input.ref = FlakeRef::fromAttrs(attrs); input.ref = FlakeRef::fromAttrs(attrs);
} catch (Error & e) { } catch (Error & e) {
e.addTrace(pos, hintfmt("in flake input")); e.addTrace(state.positions[pos], hintfmt("in flake input"));
throw; throw;
} }
else { else {
attrs.erase("url"); attrs.erase("url");
if (!attrs.empty()) if (!attrs.empty())
throw Error("unexpected flake input attribute '%s', at %s", attrs.begin()->first, pos); throw Error("unexpected flake input attribute '%s', at %s", attrs.begin()->first, state.positions[pos]);
if (url) if (url)
input.ref = parseFlakeRef(*url, baseDir, true, input.isFlake); input.ref = parseFlakeRef(*url, baseDir, true, input.isFlake);
} }
@ -168,7 +168,7 @@ static FlakeInput parseFlakeInput(EvalState & state,
} }
static std::map<FlakeId, FlakeInput> parseFlakeInputs( static std::map<FlakeId, FlakeInput> parseFlakeInputs(
EvalState & state, Value * value, const Pos & pos, EvalState & state, Value * value, const PosIdx pos,
const std::optional<Path> & baseDir, InputPath lockRootPath) const std::optional<Path> & baseDir, InputPath lockRootPath)
{ {
std::map<FlakeId, FlakeInput> inputs; std::map<FlakeId, FlakeInput> inputs;
@ -180,7 +180,7 @@ static std::map<FlakeId, FlakeInput> parseFlakeInputs(
parseFlakeInput(state, parseFlakeInput(state,
inputAttr.name, inputAttr.name,
inputAttr.value, inputAttr.value,
*inputAttr.pos, inputAttr.pos,
baseDir, baseDir,
lockRootPath)); lockRootPath));
} }
@ -218,22 +218,22 @@ static Flake getFlake(
Value vInfo; Value vInfo;
state.evalFile(flakeFile, vInfo, true); // FIXME: symlink attack state.evalFile(flakeFile, vInfo, true); // FIXME: symlink attack
expectType(state, nAttrs, vInfo, Pos(foFile, state.symbols.create(flakeFile), 0, 0)); expectType(state, nAttrs, vInfo, state.positions.add({state.symbols.create(flakeFile), foFile}, 0, 0));
if (auto description = vInfo.attrs->get(state.sDescription)) { if (auto description = vInfo.attrs->get(state.sDescription)) {
expectType(state, nString, *description->value, *description->pos); expectType(state, nString, *description->value, description->pos);
flake.description = description->value->string.s; flake.description = description->value->string.s;
} }
auto sInputs = state.symbols.create("inputs"); auto sInputs = state.symbols.create("inputs");
if (auto inputs = vInfo.attrs->get(sInputs)) if (auto inputs = vInfo.attrs->get(sInputs))
flake.inputs = parseFlakeInputs(state, inputs->value, *inputs->pos, flakeDir, lockRootPath); flake.inputs = parseFlakeInputs(state, inputs->value, inputs->pos, flakeDir, lockRootPath);
auto sOutputs = state.symbols.create("outputs"); auto sOutputs = state.symbols.create("outputs");
if (auto outputs = vInfo.attrs->get(sOutputs)) { if (auto outputs = vInfo.attrs->get(sOutputs)) {
expectType(state, nFunction, *outputs->value, *outputs->pos); expectType(state, nFunction, *outputs->value, outputs->pos);
if (outputs->value->isLambda() && outputs->value->lambda.fun->hasFormals()) { if (outputs->value->isLambda() && outputs->value->lambda.fun->hasFormals()) {
for (auto & formal : outputs->value->lambda.fun->formals->formals) { for (auto & formal : outputs->value->lambda.fun->formals->formals) {
@ -250,29 +250,29 @@ static Flake getFlake(
auto sNixConfig = state.symbols.create("nixConfig"); auto sNixConfig = state.symbols.create("nixConfig");
if (auto nixConfig = vInfo.attrs->get(sNixConfig)) { if (auto nixConfig = vInfo.attrs->get(sNixConfig)) {
expectType(state, nAttrs, *nixConfig->value, *nixConfig->pos); expectType(state, nAttrs, *nixConfig->value, nixConfig->pos);
for (auto & setting : *nixConfig->value->attrs) { for (auto & setting : *nixConfig->value->attrs) {
forceTrivialValue(state, *setting.value, *setting.pos); forceTrivialValue(state, *setting.value, setting.pos);
if (setting.value->type() == nString) if (setting.value->type() == nString)
flake.config.settings.insert({setting.name, std::string(state.forceStringNoCtx(*setting.value, *setting.pos))}); flake.config.settings.insert({setting.name, std::string(state.forceStringNoCtx(*setting.value, setting.pos))});
else if (setting.value->type() == nPath) { else if (setting.value->type() == nPath) {
PathSet emptyContext = {}; PathSet emptyContext = {};
flake.config.settings.emplace( flake.config.settings.emplace(
setting.name, setting.name,
state.coerceToString(*setting.pos, *setting.value, emptyContext, false, true, true) .toOwned()); state.coerceToString(setting.pos, *setting.value, emptyContext, false, true, true) .toOwned());
} }
else if (setting.value->type() == nInt) else if (setting.value->type() == nInt)
flake.config.settings.insert({setting.name, state.forceInt(*setting.value, *setting.pos)}); flake.config.settings.insert({setting.name, state.forceInt(*setting.value, setting.pos)});
else if (setting.value->type() == nBool) else if (setting.value->type() == nBool)
flake.config.settings.insert({setting.name, Explicit<bool> { state.forceBool(*setting.value, *setting.pos) }}); flake.config.settings.insert({setting.name, Explicit<bool> { state.forceBool(*setting.value, setting.pos) }});
else if (setting.value->type() == nList) { else if (setting.value->type() == nList) {
std::vector<std::string> ss; std::vector<std::string> ss;
for (auto elem : setting.value->listItems()) { for (auto elem : setting.value->listItems()) {
if (elem->type() != nString) if (elem->type() != nString)
throw TypeError("list element in flake configuration setting '%s' is %s while a string is expected", throw TypeError("list element in flake configuration setting '%s' is %s while a string is expected",
setting.name, showType(*setting.value)); setting.name, showType(*setting.value));
ss.emplace_back(state.forceStringNoCtx(*elem, *setting.pos)); ss.emplace_back(state.forceStringNoCtx(*elem, setting.pos));
} }
flake.config.settings.insert({setting.name, ss}); flake.config.settings.insert({setting.name, ss});
} }
@ -288,7 +288,7 @@ static Flake getFlake(
attr.name != sOutputs && attr.name != sOutputs &&
attr.name != sNixConfig) attr.name != sNixConfig)
throw Error("flake '%s' has an unsupported attribute '%s', at %s", throw Error("flake '%s' has an unsupported attribute '%s', at %s",
lockedRef, attr.name, *attr.pos); lockedRef, attr.name, state.positions[attr.pos]);
} }
return flake; return flake;
@ -704,12 +704,12 @@ void callFlake(EvalState & state,
state.callFunction(*vTmp2, *vRootSubdir, vRes, noPos); state.callFunction(*vTmp2, *vRootSubdir, vRes, noPos);
} }
static void prim_getFlake(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_getFlake(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
std::string flakeRefS(state.forceStringNoCtx(*args[0], pos)); std::string flakeRefS(state.forceStringNoCtx(*args[0], pos));
auto flakeRef = parseFlakeRef(flakeRefS, {}, true); auto flakeRef = parseFlakeRef(flakeRefS, {}, true);
if (evalSettings.pureEval && !flakeRef.input.isLocked()) if (evalSettings.pureEval && !flakeRef.input.isLocked())
throw Error("cannot call 'getFlake' on unlocked flake reference '%s', at %s (use --impure to override)", flakeRefS, pos); throw Error("cannot call 'getFlake' on unlocked flake reference '%s', at %s (use --impure to override)", flakeRefS, state.positions[pos]);
callFlake(state, callFlake(state,
lockFlake(state, flakeRef, lockFlake(state, flakeRef,

View file

@ -8,7 +8,7 @@ namespace nix {
struct FunctionCallTrace struct FunctionCallTrace
{ {
const Pos & pos; const Pos pos;
FunctionCallTrace(const Pos & pos); FunctionCallTrace(const Pos & pos);
~FunctionCallTrace(); ~FunctionCallTrace();
}; };

View file

@ -61,7 +61,7 @@ std::string DrvInfo::querySystem() const
{ {
if (system == "" && attrs) { if (system == "" && attrs) {
auto i = attrs->find(state->sSystem); auto i = attrs->find(state->sSystem);
system = i == attrs->end() ? "unknown" : state->forceStringNoCtx(*i->value, *i->pos); system = i == attrs->end() ? "unknown" : state->forceStringNoCtx(*i->value, i->pos);
} }
return system; return system;
} }
@ -75,7 +75,7 @@ std::optional<StorePath> DrvInfo::queryDrvPath() const
if (i == attrs->end()) if (i == attrs->end())
drvPath = {std::nullopt}; drvPath = {std::nullopt};
else else
drvPath = {state->coerceToStorePath(*i->pos, *i->value, context)}; drvPath = {state->coerceToStorePath(i->pos, *i->value, context)};
} }
return drvPath.value_or(std::nullopt); return drvPath.value_or(std::nullopt);
} }
@ -95,7 +95,7 @@ StorePath DrvInfo::queryOutPath() const
Bindings::iterator i = attrs->find(state->sOutPath); Bindings::iterator i = attrs->find(state->sOutPath);
PathSet context; PathSet context;
if (i != attrs->end()) if (i != attrs->end())
outPath = state->coerceToStorePath(*i->pos, *i->value, context); outPath = state->coerceToStorePath(i->pos, *i->value, context);
} }
if (!outPath) if (!outPath)
throw UnimplementedError("CA derivations are not yet supported"); throw UnimplementedError("CA derivations are not yet supported");
@ -109,23 +109,23 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool withPaths, bool onlyOutputsToInstall
/* Get the outputs list. */ /* Get the outputs list. */
Bindings::iterator i; Bindings::iterator i;
if (attrs && (i = attrs->find(state->sOutputs)) != attrs->end()) { if (attrs && (i = attrs->find(state->sOutputs)) != attrs->end()) {
state->forceList(*i->value, *i->pos); state->forceList(*i->value, i->pos);
/* For each output... */ /* For each output... */
for (auto elem : i->value->listItems()) { for (auto elem : i->value->listItems()) {
std::string output(state->forceStringNoCtx(*elem, *i->pos)); std::string output(state->forceStringNoCtx(*elem, i->pos));
if (withPaths) { if (withPaths) {
/* Evaluate the corresponding set. */ /* Evaluate the corresponding set. */
Bindings::iterator out = attrs->find(state->symbols.create(output)); Bindings::iterator out = attrs->find(state->symbols.create(output));
if (out == attrs->end()) continue; // FIXME: throw error? if (out == attrs->end()) continue; // FIXME: throw error?
state->forceAttrs(*out->value, *i->pos); state->forceAttrs(*out->value, i->pos);
/* And evaluate its outPath attribute. */ /* And evaluate its outPath attribute. */
Bindings::iterator outPath = out->value->attrs->find(state->sOutPath); Bindings::iterator outPath = out->value->attrs->find(state->sOutPath);
if (outPath == out->value->attrs->end()) continue; // FIXME: throw error? if (outPath == out->value->attrs->end()) continue; // FIXME: throw error?
PathSet context; PathSet context;
outputs.emplace(output, state->coerceToStorePath(*outPath->pos, *outPath->value, context)); outputs.emplace(output, state->coerceToStorePath(outPath->pos, *outPath->value, context));
} else } else
outputs.emplace(output, std::nullopt); outputs.emplace(output, std::nullopt);
} }
@ -168,7 +168,7 @@ Bindings * DrvInfo::getMeta()
if (!attrs) return 0; if (!attrs) return 0;
Bindings::iterator a = attrs->find(state->sMeta); Bindings::iterator a = attrs->find(state->sMeta);
if (a == attrs->end()) return 0; if (a == attrs->end()) return 0;
state->forceAttrs(*a->value, *a->pos); state->forceAttrs(*a->value, a->pos);
meta = a->value->attrs; meta = a->value->attrs;
return meta; return meta;
} }
@ -369,7 +369,7 @@ static void getDerivations(EvalState & state, Value & vIn,
`recurseForDerivations = true' attribute. */ `recurseForDerivations = true' attribute. */
if (i->value->type() == nAttrs) { if (i->value->type() == nAttrs) {
Bindings::iterator j = i->value->attrs->find(state.sRecurseForDerivations); Bindings::iterator j = i->value->attrs->find(state.sRecurseForDerivations);
if (j != i->value->attrs->end() && state.forceBool(*j->value, *j->pos)) if (j != i->value->attrs->end() && state.forceBool(*j->value, j->pos))
getDerivations(state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures); getDerivations(state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
} }
} }

View file

@ -28,9 +28,9 @@ using namespace nix;
namespace nix { namespace nix {
static inline Pos makeCurPos(const YYLTYPE & loc, ParseData * data) static inline PosIdx makeCurPos(const YYLTYPE & loc, ParseData * data)
{ {
return Pos(data->origin, data->file, loc.first_line, loc.first_column); return data->state.positions.add(data->origin, loc.first_line, loc.first_column);
} }
#define CUR_POS makeCurPos(*yylloc, data) #define CUR_POS makeCurPos(*yylloc, data)
@ -155,7 +155,7 @@ or { return OR_KW; }
} catch (const boost::bad_lexical_cast &) { } catch (const boost::bad_lexical_cast &) {
throw ParseError({ throw ParseError({
.msg = hintfmt("invalid integer '%1%'", yytext), .msg = hintfmt("invalid integer '%1%'", yytext),
.errPos = CUR_POS, .errPos = data->state.positions[CUR_POS],
}); });
} }
return INT; return INT;
@ -165,7 +165,7 @@ or { return OR_KW; }
if (errno != 0) if (errno != 0)
throw ParseError({ throw ParseError({
.msg = hintfmt("invalid float '%1%'", yytext), .msg = hintfmt("invalid float '%1%'", yytext),
.errPos = CUR_POS, .errPos = data->state.positions[CUR_POS],
}); });
return FLOAT; return FLOAT;
} }
@ -294,7 +294,7 @@ or { return OR_KW; }
<INPATH_SLASH><<EOF>> { <INPATH_SLASH><<EOF>> {
throw ParseError({ throw ParseError({
.msg = hintfmt("path has a trailing slash"), .msg = hintfmt("path has a trailing slash"),
.errPos = CUR_POS, .errPos = data->state.positions[CUR_POS],
}); });
} }

View file

@ -249,33 +249,31 @@ std::string showAttrPath(const AttrPath & attrPath)
} }
Pos noPos;
/* Computing levels/displacements for variables. */ /* Computing levels/displacements for variables. */
void Expr::bindVars(const StaticEnv & env) void Expr::bindVars(const PosTable & pt, const StaticEnv & env)
{ {
abort(); abort();
} }
void ExprInt::bindVars(const StaticEnv & env) void ExprInt::bindVars(const PosTable & pt, const StaticEnv & env)
{ {
} }
void ExprFloat::bindVars(const StaticEnv & env) void ExprFloat::bindVars(const PosTable & pt, const StaticEnv & env)
{ {
} }
void ExprString::bindVars(const StaticEnv & env) void ExprString::bindVars(const PosTable & pt, const StaticEnv & env)
{ {
} }
void ExprPath::bindVars(const StaticEnv & env) void ExprPath::bindVars(const PosTable & pt, const StaticEnv & env)
{ {
} }
void ExprVar::bindVars(const StaticEnv & env) void ExprVar::bindVars(const PosTable & pt, const StaticEnv & env)
{ {
/* Check whether the variable appears in the environment. If so, /* Check whether the variable appears in the environment. If so,
set its level and displacement. */ set its level and displacement. */
@ -302,30 +300,30 @@ void ExprVar::bindVars(const StaticEnv & env)
if (withLevel == -1) if (withLevel == -1)
throw UndefinedVarError({ throw UndefinedVarError({
.msg = hintfmt("undefined variable '%1%'", name), .msg = hintfmt("undefined variable '%1%'", name),
.errPos = pos .errPos = pt[pos]
}); });
fromWith = true; fromWith = true;
this->level = withLevel; this->level = withLevel;
} }
void ExprSelect::bindVars(const StaticEnv & env) void ExprSelect::bindVars(const PosTable & pt, const StaticEnv & env)
{ {
e->bindVars(env); e->bindVars(pt, env);
if (def) def->bindVars(env); if (def) def->bindVars(pt, env);
for (auto & i : attrPath) for (auto & i : attrPath)
if (!i.symbol.set()) if (!i.symbol.set())
i.expr->bindVars(env); i.expr->bindVars(pt, env);
} }
void ExprOpHasAttr::bindVars(const StaticEnv & env) void ExprOpHasAttr::bindVars(const PosTable & pt, const StaticEnv & env)
{ {
e->bindVars(env); e->bindVars(pt, env);
for (auto & i : attrPath) for (auto & i : attrPath)
if (!i.symbol.set()) if (!i.symbol.set())
i.expr->bindVars(env); i.expr->bindVars(pt, env);
} }
void ExprAttrs::bindVars(const StaticEnv & env) void ExprAttrs::bindVars(const PosTable & pt, const StaticEnv & env)
{ {
const StaticEnv * dynamicEnv = &env; const StaticEnv * dynamicEnv = &env;
StaticEnv newEnv(false, &env, recursive ? attrs.size() : 0); StaticEnv newEnv(false, &env, recursive ? attrs.size() : 0);
@ -340,26 +338,26 @@ void ExprAttrs::bindVars(const StaticEnv & env)
// No need to sort newEnv since attrs is in sorted order. // No need to sort newEnv since attrs is in sorted order.
for (auto & i : attrs) for (auto & i : attrs)
i.second.e->bindVars(i.second.inherited ? env : newEnv); i.second.e->bindVars(pt, i.second.inherited ? env : newEnv);
} }
else else
for (auto & i : attrs) for (auto & i : attrs)
i.second.e->bindVars(env); i.second.e->bindVars(pt, env);
for (auto & i : dynamicAttrs) { for (auto & i : dynamicAttrs) {
i.nameExpr->bindVars(*dynamicEnv); i.nameExpr->bindVars(pt, *dynamicEnv);
i.valueExpr->bindVars(*dynamicEnv); i.valueExpr->bindVars(pt, *dynamicEnv);
} }
} }
void ExprList::bindVars(const StaticEnv & env) void ExprList::bindVars(const PosTable & pt, const StaticEnv & env)
{ {
for (auto & i : elems) for (auto & i : elems)
i->bindVars(env); i->bindVars(pt, env);
} }
void ExprLambda::bindVars(const StaticEnv & env) void ExprLambda::bindVars(const PosTable & pt, const StaticEnv & env)
{ {
StaticEnv newEnv( StaticEnv newEnv(
false, &env, false, &env,
@ -377,20 +375,20 @@ void ExprLambda::bindVars(const StaticEnv & env)
newEnv.sort(); newEnv.sort();
for (auto & i : formals->formals) for (auto & i : formals->formals)
if (i.def) i.def->bindVars(newEnv); if (i.def) i.def->bindVars(pt, newEnv);
} }
body->bindVars(newEnv); body->bindVars(pt, newEnv);
} }
void ExprCall::bindVars(const StaticEnv & env) void ExprCall::bindVars(const PosTable & pt, const StaticEnv & env)
{ {
fun->bindVars(env); fun->bindVars(pt, env);
for (auto e : args) for (auto e : args)
e->bindVars(env); e->bindVars(pt, env);
} }
void ExprLet::bindVars(const StaticEnv & env) void ExprLet::bindVars(const PosTable & pt, const StaticEnv & env)
{ {
StaticEnv newEnv(false, &env, attrs->attrs.size()); StaticEnv newEnv(false, &env, attrs->attrs.size());
@ -401,12 +399,12 @@ void ExprLet::bindVars(const StaticEnv & env)
// No need to sort newEnv since attrs->attrs is in sorted order. // No need to sort newEnv since attrs->attrs is in sorted order.
for (auto & i : attrs->attrs) for (auto & i : attrs->attrs)
i.second.e->bindVars(i.second.inherited ? env : newEnv); i.second.e->bindVars(pt, i.second.inherited ? env : newEnv);
body->bindVars(newEnv); body->bindVars(pt, newEnv);
} }
void ExprWith::bindVars(const StaticEnv & env) void ExprWith::bindVars(const PosTable & pt, const StaticEnv & env)
{ {
/* Does this `with' have an enclosing `with'? If so, record its /* Does this `with' have an enclosing `with'? If so, record its
level so that `lookupVar' can look up variables in the previous level so that `lookupVar' can look up variables in the previous
@ -420,36 +418,36 @@ void ExprWith::bindVars(const StaticEnv & env)
break; break;
} }
attrs->bindVars(env); attrs->bindVars(pt, env);
StaticEnv newEnv(true, &env); StaticEnv newEnv(true, &env);
body->bindVars(newEnv); body->bindVars(pt, newEnv);
} }
void ExprIf::bindVars(const StaticEnv & env) void ExprIf::bindVars(const PosTable & pt, const StaticEnv & env)
{ {
cond->bindVars(env); cond->bindVars(pt, env);
then->bindVars(env); then->bindVars(pt, env);
else_->bindVars(env); else_->bindVars(pt, env);
} }
void ExprAssert::bindVars(const StaticEnv & env) void ExprAssert::bindVars(const PosTable & pt, const StaticEnv & env)
{ {
cond->bindVars(env); cond->bindVars(pt, env);
body->bindVars(env); body->bindVars(pt, env);
} }
void ExprOpNot::bindVars(const StaticEnv & env) void ExprOpNot::bindVars(const PosTable & pt, const StaticEnv & env)
{ {
e->bindVars(env); e->bindVars(pt, env);
} }
void ExprConcatStrings::bindVars(const StaticEnv & env) void ExprConcatStrings::bindVars(const PosTable & pt, const StaticEnv & env)
{ {
for (auto & i : *es) for (auto & i : *es)
i.second->bindVars(env); i.second->bindVars(pt, env);
} }
void ExprPos::bindVars(const StaticEnv & env) void ExprPos::bindVars(const PosTable & pt, const StaticEnv & env)
{ {
} }
@ -468,9 +466,9 @@ void ExprLambda::setName(Symbol & name)
} }
std::string ExprLambda::showNamePos() const std::string ExprLambda::showNamePos(const PosTable & pt) const
{ {
return fmt("%1% at %2%", name.set() ? "'" + (std::string) name + "'" : "anonymous function", pos); return fmt("%1% at %2%", name.set() ? "'" + (std::string) name + "'" : "anonymous function", pt[pos]);
} }

View file

@ -1,5 +1,8 @@
#pragma once #pragma once
#include <map>
#include <vector>
#include "value.hh" #include "value.hh"
#include "symbol-table.hh" #include "symbol-table.hh"
#include "error.hh" #include "error.hh"
@ -24,31 +27,91 @@ MakeError(RestrictedPathError, Error);
struct Pos struct Pos
{ {
Symbol file; Symbol file;
FileOrigin origin;
uint32_t line; uint32_t line;
FileOrigin origin:2; uint32_t column;
uint32_t column:30;
Pos() : line(0), origin(foString), column(0) { }; explicit operator bool() const { return line > 0; }
Pos(FileOrigin origin, const Symbol & file, uint32_t line, uint32_t column) };
: file(file), line(line), origin(origin), column(column) { };
operator bool() const class PosIdx {
friend class PosTable;
private:
uint32_t id;
explicit PosIdx(uint32_t id): id(id) {}
public:
PosIdx() : id(0) {}
explicit operator bool() const { return id > 0; }
bool operator<(const PosIdx other) const { return id < other.id; }
};
class PosTable
{
public:
class Origin {
friend PosTable;
private:
// must always be invalid by default, add() replaces this with the actual value.
// subsequent add() calls use this index as a token to quickly check whether the
// current origins.back() can be reused or not.
mutable uint32_t idx = std::numeric_limits<uint32_t>::max();
explicit Origin(uint32_t idx): idx(idx), file{}, origin{} {}
public:
const Symbol file;
const FileOrigin origin;
Origin(Symbol file, FileOrigin origin): file(file), origin(origin) {}
};
struct Offset {
uint32_t line, column;
};
private:
std::vector<Origin> origins;
ChunkedVector<Offset, 8192> offsets;
public:
PosTable(): offsets(1024)
{ {
return line != 0; origins.reserve(1024);
} }
bool operator < (const Pos & p2) const PosIdx add(const Origin & origin, uint32_t line, uint32_t column)
{ {
if (!line) return p2.line; const auto idx = offsets.add({line, column}).second;
if (!p2.line) return false; if (origins.empty() || origins.back().idx != origin.idx) {
int d = ((const std::string &) file).compare((const std::string &) p2.file); origin.idx = idx;
if (d < 0) return true; origins.push_back(origin);
if (d > 0) return false; }
if (line < p2.line) return true; return PosIdx(idx + 1);
if (line > p2.line) return false; }
return column < p2.column;
Pos operator[](PosIdx p) const
{
if (p.id == 0 || p.id > offsets.size())
return {};
const auto idx = p.id - 1;
/* we want the last key <= idx, so we'll take prev(first key > idx).
this is guaranteed to never rewind origin.begin because the first
key is always 0. */
const auto pastOrigin = std::upper_bound(
origins.begin(), origins.end(), Origin(idx),
[] (const auto & a, const auto & b) { return a.idx < b.idx; });
const auto origin = *std::prev(pastOrigin);
const auto offset = offsets[idx];
return {origin.file, origin.origin, offset.line, offset.column};
} }
}; };
extern Pos noPos; inline PosIdx noPos = {};
std::ostream & operator << (std::ostream & str, const Pos & pos); std::ostream & operator << (std::ostream & str, const Pos & pos);
@ -79,7 +142,7 @@ struct Expr
{ {
virtual ~Expr() { }; virtual ~Expr() { };
virtual void show(std::ostream & str) const; virtual void show(std::ostream & str) const;
virtual void bindVars(const StaticEnv & env); virtual void bindVars(const PosTable & pt, const StaticEnv & env);
virtual void eval(EvalState & state, Env & env, Value & v); virtual void eval(EvalState & state, Env & env, Value & v);
virtual Value * maybeThunk(EvalState & state, Env & env); virtual Value * maybeThunk(EvalState & state, Env & env);
virtual void setName(Symbol & name); virtual void setName(Symbol & name);
@ -90,7 +153,7 @@ std::ostream & operator << (std::ostream & str, const Expr & e);
#define COMMON_METHODS \ #define COMMON_METHODS \
void show(std::ostream & str) const; \ void show(std::ostream & str) const; \
void eval(EvalState & state, Env & env, Value & v); \ void eval(EvalState & state, Env & env, Value & v); \
void bindVars(const StaticEnv & env); void bindVars(const PosTable & pt, const StaticEnv & env);
struct ExprInt : Expr struct ExprInt : Expr
{ {
@ -133,7 +196,7 @@ typedef uint32_t Displacement;
struct ExprVar : Expr struct ExprVar : Expr
{ {
Pos pos; PosIdx pos;
Symbol name; Symbol name;
/* Whether the variable comes from an environment (e.g. a rec, let /* Whether the variable comes from an environment (e.g. a rec, let
@ -150,18 +213,18 @@ struct ExprVar : Expr
Displacement displ; Displacement displ;
ExprVar(const Symbol & name) : name(name) { }; ExprVar(const Symbol & name) : name(name) { };
ExprVar(const Pos & pos, const Symbol & name) : pos(pos), name(name) { }; ExprVar(const PosIdx & pos, const Symbol & name) : pos(pos), name(name) { };
COMMON_METHODS COMMON_METHODS
Value * maybeThunk(EvalState & state, Env & env); Value * maybeThunk(EvalState & state, Env & env);
}; };
struct ExprSelect : Expr struct ExprSelect : Expr
{ {
Pos pos; PosIdx pos;
Expr * e, * def; Expr * e, * def;
AttrPath attrPath; AttrPath attrPath;
ExprSelect(const Pos & pos, Expr * e, const AttrPath & attrPath, Expr * def) : pos(pos), e(e), def(def), attrPath(attrPath) { }; ExprSelect(const PosIdx & pos, Expr * e, const AttrPath & attrPath, Expr * def) : pos(pos), e(e), def(def), attrPath(attrPath) { };
ExprSelect(const Pos & pos, Expr * e, const Symbol & name) : pos(pos), e(e), def(0) { attrPath.push_back(AttrName(name)); }; ExprSelect(const PosIdx & pos, Expr * e, const Symbol & name) : pos(pos), e(e), def(0) { attrPath.push_back(AttrName(name)); };
COMMON_METHODS COMMON_METHODS
}; };
@ -176,13 +239,13 @@ struct ExprOpHasAttr : Expr
struct ExprAttrs : Expr struct ExprAttrs : Expr
{ {
bool recursive; bool recursive;
Pos pos; PosIdx pos;
struct AttrDef { struct AttrDef {
bool inherited; bool inherited;
Expr * e; Expr * e;
Pos pos; PosIdx pos;
Displacement displ; // displacement Displacement displ; // displacement
AttrDef(Expr * e, const Pos & pos, bool inherited=false) AttrDef(Expr * e, const PosIdx & pos, bool inherited=false)
: inherited(inherited), e(e), pos(pos) { }; : inherited(inherited), e(e), pos(pos) { };
AttrDef() { }; AttrDef() { };
}; };
@ -190,14 +253,14 @@ struct ExprAttrs : Expr
AttrDefs attrs; AttrDefs attrs;
struct DynamicAttrDef { struct DynamicAttrDef {
Expr * nameExpr, * valueExpr; Expr * nameExpr, * valueExpr;
Pos pos; PosIdx pos;
DynamicAttrDef(Expr * nameExpr, Expr * valueExpr, const Pos & pos) DynamicAttrDef(Expr * nameExpr, Expr * valueExpr, const PosIdx & pos)
: nameExpr(nameExpr), valueExpr(valueExpr), pos(pos) { }; : nameExpr(nameExpr), valueExpr(valueExpr), pos(pos) { };
}; };
typedef std::vector<DynamicAttrDef> DynamicAttrDefs; typedef std::vector<DynamicAttrDef> DynamicAttrDefs;
DynamicAttrDefs dynamicAttrs; DynamicAttrDefs dynamicAttrs;
ExprAttrs(const Pos &pos) : recursive(false), pos(pos) { }; ExprAttrs(const PosIdx &pos) : recursive(false), pos(pos) { };
ExprAttrs() : recursive(false), pos(noPos) { }; ExprAttrs() : recursive(false) { };
COMMON_METHODS COMMON_METHODS
}; };
@ -210,10 +273,10 @@ struct ExprList : Expr
struct Formal struct Formal
{ {
Pos pos; PosIdx pos;
Symbol name; Symbol name;
Expr * def; Expr * def;
Formal(const Pos & pos, const Symbol & name, Expr * def) : pos(pos), name(name), def(def) { }; Formal(const PosIdx & pos, const Symbol & name, Expr * def) : pos(pos), name(name), def(def) { };
}; };
struct Formals struct Formals
@ -241,17 +304,17 @@ struct Formals
struct ExprLambda : Expr struct ExprLambda : Expr
{ {
Pos pos; PosIdx pos;
Symbol name; Symbol name;
Symbol arg; Symbol arg;
Formals * formals; Formals * formals;
Expr * body; Expr * body;
ExprLambda(const Pos & pos, const Symbol & arg, Formals * formals, Expr * body) ExprLambda(const PosIdx & pos, const Symbol & arg, Formals * formals, Expr * body)
: pos(pos), arg(arg), formals(formals), body(body) : pos(pos), arg(arg), formals(formals), body(body)
{ {
}; };
void setName(Symbol & name); void setName(Symbol & name);
std::string showNamePos() const; std::string showNamePos(const PosTable & pt) const;
inline bool hasFormals() const { return formals != nullptr; } inline bool hasFormals() const { return formals != nullptr; }
COMMON_METHODS COMMON_METHODS
}; };
@ -260,8 +323,8 @@ struct ExprCall : Expr
{ {
Expr * fun; Expr * fun;
std::vector<Expr *> args; std::vector<Expr *> args;
Pos pos; PosIdx pos;
ExprCall(const Pos & pos, Expr * fun, std::vector<Expr *> && args) ExprCall(const PosIdx & pos, Expr * fun, std::vector<Expr *> && args)
: fun(fun), args(args), pos(pos) : fun(fun), args(args), pos(pos)
{ } { }
COMMON_METHODS COMMON_METHODS
@ -277,26 +340,26 @@ struct ExprLet : Expr
struct ExprWith : Expr struct ExprWith : Expr
{ {
Pos pos; PosIdx pos;
Expr * attrs, * body; Expr * attrs, * body;
size_t prevWith; size_t prevWith;
ExprWith(const Pos & pos, Expr * attrs, Expr * body) : pos(pos), attrs(attrs), body(body) { }; ExprWith(const PosIdx & pos, Expr * attrs, Expr * body) : pos(pos), attrs(attrs), body(body) { };
COMMON_METHODS COMMON_METHODS
}; };
struct ExprIf : Expr struct ExprIf : Expr
{ {
Pos pos; PosIdx pos;
Expr * cond, * then, * else_; Expr * cond, * then, * else_;
ExprIf(const Pos & pos, Expr * cond, Expr * then, Expr * else_) : pos(pos), cond(cond), then(then), else_(else_) { }; ExprIf(const PosIdx & pos, Expr * cond, Expr * then, Expr * else_) : pos(pos), cond(cond), then(then), else_(else_) { };
COMMON_METHODS COMMON_METHODS
}; };
struct ExprAssert : Expr struct ExprAssert : Expr
{ {
Pos pos; PosIdx pos;
Expr * cond, * body; Expr * cond, * body;
ExprAssert(const Pos & pos, Expr * cond, Expr * body) : pos(pos), cond(cond), body(body) { }; ExprAssert(const PosIdx & pos, Expr * cond, Expr * body) : pos(pos), cond(cond), body(body) { };
COMMON_METHODS COMMON_METHODS
}; };
@ -310,17 +373,17 @@ struct ExprOpNot : Expr
#define MakeBinOp(name, s) \ #define MakeBinOp(name, s) \
struct name : Expr \ struct name : Expr \
{ \ { \
Pos pos; \ PosIdx pos; \
Expr * e1, * e2; \ Expr * e1, * e2; \
name(Expr * e1, Expr * e2) : e1(e1), e2(e2) { }; \ name(Expr * e1, Expr * e2) : e1(e1), e2(e2) { }; \
name(const Pos & pos, Expr * e1, Expr * e2) : pos(pos), e1(e1), e2(e2) { }; \ name(const PosIdx & pos, Expr * e1, Expr * e2) : pos(pos), e1(e1), e2(e2) { }; \
void show(std::ostream & str) const \ void show(std::ostream & str) const \
{ \ { \
str << "(" << *e1 << " " s " " << *e2 << ")"; \ str << "(" << *e1 << " " s " " << *e2 << ")"; \
} \ } \
void bindVars(const StaticEnv & env) \ void bindVars(const PosTable & pt, const StaticEnv & env) \
{ \ { \
e1->bindVars(env); e2->bindVars(env); \ e1->bindVars(pt, env); e2->bindVars(pt, env); \
} \ } \
void eval(EvalState & state, Env & env, Value & v); \ void eval(EvalState & state, Env & env, Value & v); \
}; };
@ -335,18 +398,18 @@ MakeBinOp(ExprOpConcatLists, "++")
struct ExprConcatStrings : Expr struct ExprConcatStrings : Expr
{ {
Pos pos; PosIdx pos;
bool forceString; bool forceString;
std::vector<std::pair<Pos, Expr *> > * es; std::vector<std::pair<PosIdx, Expr *> > * es;
ExprConcatStrings(const Pos & pos, bool forceString, std::vector<std::pair<Pos, Expr *> > * es) ExprConcatStrings(const PosIdx & pos, bool forceString, std::vector<std::pair<PosIdx, Expr *> > * es)
: pos(pos), forceString(forceString), es(es) { }; : pos(pos), forceString(forceString), es(es) { };
COMMON_METHODS COMMON_METHODS
}; };
struct ExprPos : Expr struct ExprPos : Expr
{ {
Pos pos; PosIdx pos;
ExprPos(const Pos & pos) : pos(pos) { }; ExprPos(const PosIdx & pos) : pos(pos) { };
COMMON_METHODS COMMON_METHODS
}; };

View file

@ -32,12 +32,12 @@ namespace nix {
SymbolTable & symbols; SymbolTable & symbols;
Expr * result; Expr * result;
Path basePath; Path basePath;
Symbol file; PosTable::Origin origin;
FileOrigin origin;
std::optional<ErrorInfo> error; std::optional<ErrorInfo> error;
ParseData(EvalState & state) ParseData(EvalState & state, PosTable::Origin origin)
: state(state) : state(state)
, symbols(state.symbols) , symbols(state.symbols)
, origin(std::move(origin))
{ }; { };
}; };
@ -96,7 +96,7 @@ static void dupAttr(Symbol attr, const Pos & pos, const Pos & prevPos)
static void addAttr(ExprAttrs * attrs, AttrPath & attrPath, static void addAttr(ExprAttrs * attrs, AttrPath & attrPath,
Expr * e, const Pos & pos) Expr * e, const PosIdx pos, const nix::PosTable & pt)
{ {
AttrPath::iterator i; AttrPath::iterator i;
// All attrpaths have at least one attr // All attrpaths have at least one attr
@ -109,10 +109,10 @@ static void addAttr(ExprAttrs * attrs, AttrPath & attrPath,
if (j != attrs->attrs.end()) { if (j != attrs->attrs.end()) {
if (!j->second.inherited) { if (!j->second.inherited) {
ExprAttrs * attrs2 = dynamic_cast<ExprAttrs *>(j->second.e); ExprAttrs * attrs2 = dynamic_cast<ExprAttrs *>(j->second.e);
if (!attrs2) dupAttr(attrPath, pos, j->second.pos); if (!attrs2) dupAttr(attrPath, pt[pos], pt[j->second.pos]);
attrs = attrs2; attrs = attrs2;
} else } else
dupAttr(attrPath, pos, j->second.pos); dupAttr(attrPath, pt[pos], pt[j->second.pos]);
} else { } else {
ExprAttrs * nested = new ExprAttrs; ExprAttrs * nested = new ExprAttrs;
attrs->attrs[i->symbol] = ExprAttrs::AttrDef(nested, pos); attrs->attrs[i->symbol] = ExprAttrs::AttrDef(nested, pos);
@ -139,11 +139,11 @@ static void addAttr(ExprAttrs * attrs, AttrPath & attrPath,
for (auto & ad : ae->attrs) { for (auto & ad : ae->attrs) {
auto j2 = jAttrs->attrs.find(ad.first); auto j2 = jAttrs->attrs.find(ad.first);
if (j2 != jAttrs->attrs.end()) // Attr already defined in iAttrs, error. if (j2 != jAttrs->attrs.end()) // Attr already defined in iAttrs, error.
dupAttr(ad.first, j2->second.pos, ad.second.pos); dupAttr(ad.first, pt[j2->second.pos], pt[ad.second.pos]);
jAttrs->attrs.emplace(ad.first, ad.second); jAttrs->attrs.emplace(ad.first, ad.second);
} }
} else { } else {
dupAttr(attrPath, pos, j->second.pos); dupAttr(attrPath, pt[pos], pt[j->second.pos]);
} }
} else { } else {
// This attr path is not defined. Let's create it. // This attr path is not defined. Let's create it.
@ -157,14 +157,14 @@ static void addAttr(ExprAttrs * attrs, AttrPath & attrPath,
static Formals * toFormals(ParseData & data, ParserFormals * formals, static Formals * toFormals(ParseData & data, ParserFormals * formals,
Pos pos = noPos, Symbol arg = {}) PosIdx pos = noPos, Symbol arg = {})
{ {
std::sort(formals->formals.begin(), formals->formals.end(), std::sort(formals->formals.begin(), formals->formals.end(),
[] (const auto & a, const auto & b) { [] (const auto & a, const auto & b) {
return std::tie(a.name, a.pos) < std::tie(b.name, b.pos); return std::tie(a.name, a.pos) < std::tie(b.name, b.pos);
}); });
std::optional<std::pair<Symbol, Pos>> duplicate; std::optional<std::pair<Symbol, PosIdx>> duplicate;
for (size_t i = 0; i + 1 < formals->formals.size(); i++) { for (size_t i = 0; i + 1 < formals->formals.size(); i++) {
if (formals->formals[i].name != formals->formals[i + 1].name) if (formals->formals[i].name != formals->formals[i + 1].name)
continue; continue;
@ -174,7 +174,7 @@ static Formals * toFormals(ParseData & data, ParserFormals * formals,
if (duplicate) if (duplicate)
throw ParseError({ throw ParseError({
.msg = hintfmt("duplicate formal function argument '%1%'", duplicate->first), .msg = hintfmt("duplicate formal function argument '%1%'", duplicate->first),
.errPos = duplicate->second .errPos = data.state.positions[duplicate->second]
}); });
Formals result; Formals result;
@ -184,7 +184,7 @@ static Formals * toFormals(ParseData & data, ParserFormals * formals,
if (arg.set() && result.has(arg)) if (arg.set() && result.has(arg))
throw ParseError({ throw ParseError({
.msg = hintfmt("duplicate formal function argument '%1%'", arg), .msg = hintfmt("duplicate formal function argument '%1%'", arg),
.errPos = pos .errPos = data.state.positions[pos]
}); });
delete formals; delete formals;
@ -192,8 +192,8 @@ static Formals * toFormals(ParseData & data, ParserFormals * formals,
} }
static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, static Expr * stripIndentation(const PosIdx pos, SymbolTable & symbols,
std::vector<std::pair<Pos, std::variant<Expr *, StringToken> > > & es) std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken> > > & es)
{ {
if (es.empty()) return new ExprString(""); if (es.empty()) return new ExprString("");
@ -233,7 +233,7 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols,
} }
/* Strip spaces from each line. */ /* Strip spaces from each line. */
std::vector<std::pair<Pos, Expr *> > * es2 = new std::vector<std::pair<Pos, Expr *> >; auto * es2 = new std::vector<std::pair<PosIdx, Expr *> >;
atStartOfLine = true; atStartOfLine = true;
size_t curDropped = 0; size_t curDropped = 0;
size_t n = es.size(); size_t n = es.size();
@ -284,9 +284,9 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols,
} }
static inline Pos makeCurPos(const YYLTYPE & loc, ParseData * data) static inline PosIdx makeCurPos(const YYLTYPE & loc, ParseData * data)
{ {
return Pos(data->origin, data->file, loc.first_line, loc.first_column); return data->state.positions.add(data->origin, loc.first_line, loc.first_column);
} }
#define CUR_POS makeCurPos(*yylocp, data) #define CUR_POS makeCurPos(*yylocp, data)
@ -299,7 +299,7 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
{ {
data->error = { data->error = {
.msg = hintfmt(error), .msg = hintfmt(error),
.errPos = makeCurPos(*loc, data) .errPos = data->state.positions[makeCurPos(*loc, data)]
}; };
} }
@ -320,8 +320,8 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
StringToken uri; StringToken uri;
StringToken str; StringToken str;
std::vector<nix::AttrName> * attrNames; std::vector<nix::AttrName> * attrNames;
std::vector<std::pair<nix::Pos, nix::Expr *> > * string_parts; std::vector<std::pair<nix::PosIdx, nix::Expr *> > * string_parts;
std::vector<std::pair<nix::Pos, std::variant<nix::Expr *, StringToken> > > * ind_string_parts; std::vector<std::pair<nix::PosIdx, std::variant<nix::Expr *, StringToken> > > * ind_string_parts;
} }
%type <e> start expr expr_function expr_if expr_op %type <e> start expr expr_function expr_if expr_op
@ -388,7 +388,7 @@ expr_function
{ if (!$2->dynamicAttrs.empty()) { if (!$2->dynamicAttrs.empty())
throw ParseError({ throw ParseError({
.msg = hintfmt("dynamic attributes not allowed in let"), .msg = hintfmt("dynamic attributes not allowed in let"),
.errPos = CUR_POS .errPos = data->state.positions[CUR_POS]
}); });
$$ = new ExprLet($2, $4); $$ = new ExprLet($2, $4);
} }
@ -415,7 +415,7 @@ expr_op
| expr_op UPDATE expr_op { $$ = new ExprOpUpdate(CUR_POS, $1, $3); } | expr_op UPDATE expr_op { $$ = new ExprOpUpdate(CUR_POS, $1, $3); }
| expr_op '?' attrpath { $$ = new ExprOpHasAttr($1, *$3); } | expr_op '?' attrpath { $$ = new ExprOpHasAttr($1, *$3); }
| expr_op '+' expr_op | expr_op '+' expr_op
{ $$ = new ExprConcatStrings(CUR_POS, false, new std::vector<std::pair<Pos, Expr *> >({{makeCurPos(@1, data), $1}, {makeCurPos(@3, data), $3}})); } { $$ = new ExprConcatStrings(CUR_POS, false, new std::vector<std::pair<PosIdx, Expr *> >({{makeCurPos(@1, data), $1}, {makeCurPos(@3, data), $3}})); }
| expr_op '-' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__sub")), {$1, $3}); } | expr_op '-' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__sub")), {$1, $3}); }
| expr_op '*' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__mul")), {$1, $3}); } | expr_op '*' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__mul")), {$1, $3}); }
| expr_op '/' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__div")), {$1, $3}); } | expr_op '/' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__div")), {$1, $3}); }
@ -477,7 +477,7 @@ expr_simple
if (noURLLiterals) if (noURLLiterals)
throw ParseError({ throw ParseError({
.msg = hintfmt("URL literals are disabled"), .msg = hintfmt("URL literals are disabled"),
.errPos = CUR_POS .errPos = data->state.positions[CUR_POS]
}); });
$$ = new ExprString(std::string($1)); $$ = new ExprString(std::string($1));
} }
@ -503,9 +503,9 @@ string_parts_interpolated
: string_parts_interpolated STR : string_parts_interpolated STR
{ $$ = $1; $1->emplace_back(makeCurPos(@2, data), new ExprString(std::string($2))); } { $$ = $1; $1->emplace_back(makeCurPos(@2, data), new ExprString(std::string($2))); }
| string_parts_interpolated DOLLAR_CURLY expr '}' { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $3); } | string_parts_interpolated DOLLAR_CURLY expr '}' { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $3); }
| DOLLAR_CURLY expr '}' { $$ = new std::vector<std::pair<Pos, Expr *> >; $$->emplace_back(makeCurPos(@1, data), $2); } | DOLLAR_CURLY expr '}' { $$ = new std::vector<std::pair<PosIdx, Expr *> >; $$->emplace_back(makeCurPos(@1, data), $2); }
| STR DOLLAR_CURLY expr '}' { | STR DOLLAR_CURLY expr '}' {
$$ = new std::vector<std::pair<Pos, Expr *> >; $$ = new std::vector<std::pair<PosIdx, Expr *> >;
$$->emplace_back(makeCurPos(@1, data), new ExprString(std::string($1))); $$->emplace_back(makeCurPos(@1, data), new ExprString(std::string($1)));
$$->emplace_back(makeCurPos(@2, data), $3); $$->emplace_back(makeCurPos(@2, data), $3);
} }
@ -528,17 +528,18 @@ path_start
ind_string_parts ind_string_parts
: ind_string_parts IND_STR { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $2); } : ind_string_parts IND_STR { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $2); }
| ind_string_parts DOLLAR_CURLY expr '}' { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $3); } | ind_string_parts DOLLAR_CURLY expr '}' { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $3); }
| { $$ = new std::vector<std::pair<Pos, std::variant<Expr *, StringToken> > >; } | { $$ = new std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken> > >; }
; ;
binds binds
: binds attrpath '=' expr ';' { $$ = $1; addAttr($$, *$2, $4, makeCurPos(@2, data)); } : binds attrpath '=' expr ';' { $$ = $1; addAttr($$, *$2, $4, makeCurPos(@2, data), data->state.positions); }
| binds INHERIT attrs ';' | binds INHERIT attrs ';'
{ $$ = $1; { $$ = $1;
for (auto & i : *$3) { for (auto & i : *$3) {
if ($$->attrs.find(i.symbol) != $$->attrs.end()) if ($$->attrs.find(i.symbol) != $$->attrs.end())
dupAttr(i.symbol, makeCurPos(@3, data), $$->attrs[i.symbol].pos); dupAttr(i.symbol, data->state.positions[makeCurPos(@3, data)],
Pos pos = makeCurPos(@3, data); data->state.positions[$$->attrs[i.symbol].pos]);
auto pos = makeCurPos(@3, data);
$$->attrs.emplace(i.symbol, ExprAttrs::AttrDef(new ExprVar(CUR_POS, i.symbol), pos, true)); $$->attrs.emplace(i.symbol, ExprAttrs::AttrDef(new ExprVar(CUR_POS, i.symbol), pos, true));
} }
} }
@ -547,7 +548,8 @@ binds
/* !!! Should ensure sharing of the expression in $4. */ /* !!! Should ensure sharing of the expression in $4. */
for (auto & i : *$6) { for (auto & i : *$6) {
if ($$->attrs.find(i.symbol) != $$->attrs.end()) if ($$->attrs.find(i.symbol) != $$->attrs.end())
dupAttr(i.symbol, makeCurPos(@6, data), $$->attrs[i.symbol].pos); dupAttr(i.symbol, data->state.positions[makeCurPos(@6, data)],
data->state.positions[$$->attrs[i.symbol].pos]);
$$->attrs.emplace(i.symbol, ExprAttrs::AttrDef(new ExprSelect(CUR_POS, $4, i.symbol), makeCurPos(@6, data))); $$->attrs.emplace(i.symbol, ExprAttrs::AttrDef(new ExprSelect(CUR_POS, $4, i.symbol), makeCurPos(@6, data)));
} }
} }
@ -565,7 +567,7 @@ attrs
} else } else
throw ParseError({ throw ParseError({
.msg = hintfmt("dynamic attributes not allowed in inherit"), .msg = hintfmt("dynamic attributes not allowed in inherit"),
.errPos = makeCurPos(@2, data) .errPos = data->state.positions[makeCurPos(@2, data)]
}); });
} }
| { $$ = new AttrPath; } | { $$ = new AttrPath; }
@ -646,19 +648,19 @@ Expr * EvalState::parse(char * text, size_t length, FileOrigin origin,
const PathView path, const PathView basePath, StaticEnv & staticEnv) const PathView path, const PathView basePath, StaticEnv & staticEnv)
{ {
yyscan_t scanner; yyscan_t scanner;
ParseData data(*this); Symbol file;
data.origin = origin;
switch (origin) { switch (origin) {
case foFile: case foFile:
data.file = data.symbols.create(path); file = symbols.create(path);
break; break;
case foStdin: case foStdin:
case foString: case foString:
data.file = data.symbols.create(text); file = symbols.create(text);
break; break;
default: default:
assert(false); assert(false);
} }
ParseData data(*this, {file, origin});
data.basePath = basePath; data.basePath = basePath;
yylex_init(&scanner); yylex_init(&scanner);
@ -668,7 +670,7 @@ Expr * EvalState::parse(char * text, size_t length, FileOrigin origin,
if (res) throw ParseError(data.error.value()); if (res) throw ParseError(data.error.value());
data.result->bindVars(staticEnv); data.result->bindVars(positions, staticEnv);
return data.result; return data.result;
} }
@ -760,7 +762,7 @@ Path EvalState::findFile(const std::string_view path)
} }
Path EvalState::findFile(SearchPath & searchPath, const std::string_view path, const Pos & pos) Path EvalState::findFile(SearchPath & searchPath, const std::string_view path, const PosIdx pos)
{ {
for (auto & i : searchPath) { for (auto & i : searchPath) {
std::string suffix; std::string suffix;
@ -787,7 +789,7 @@ Path EvalState::findFile(SearchPath & searchPath, const std::string_view path, c
? "cannot look up '<%s>' in pure evaluation mode (use '--impure' to override)" ? "cannot look up '<%s>' in pure evaluation mode (use '--impure' to override)"
: "file '%s' was not found in the Nix search path (add it using $NIX_PATH or -I)", : "file '%s' was not found in the Nix search path (add it using $NIX_PATH or -I)",
path), path),
.errPos = pos .errPos = positions[pos]
}); });
} }

File diff suppressed because it is too large Load diff

View file

@ -38,9 +38,9 @@ struct RegisterPrimOp
them. */ them. */
/* Load a ValueInitializer from a DSO and return whatever it initializes */ /* Load a ValueInitializer from a DSO and return whatever it initializes */
void prim_importNative(EvalState & state, const Pos & pos, Value * * args, Value & v); void prim_importNative(EvalState & state, const PosIdx pos, Value * * args, Value & v);
/* Execute a program and parse its output */ /* Execute a program and parse its output */
void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v); void prim_exec(EvalState & state, const PosIdx pos, Value * * args, Value & v);
} }

View file

@ -5,7 +5,7 @@
namespace nix { namespace nix {
static void prim_unsafeDiscardStringContext(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_unsafeDiscardStringContext(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
PathSet context; PathSet context;
auto s = state.coerceToString(pos, *args[0], context); auto s = state.coerceToString(pos, *args[0], context);
@ -15,7 +15,7 @@ static void prim_unsafeDiscardStringContext(EvalState & state, const Pos & pos,
static RegisterPrimOp primop_unsafeDiscardStringContext("__unsafeDiscardStringContext", 1, prim_unsafeDiscardStringContext); static RegisterPrimOp primop_unsafeDiscardStringContext("__unsafeDiscardStringContext", 1, prim_unsafeDiscardStringContext);
static void prim_hasContext(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_hasContext(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
PathSet context; PathSet context;
state.forceString(*args[0], context, pos); state.forceString(*args[0], context, pos);
@ -31,7 +31,7 @@ static RegisterPrimOp primop_hasContext("__hasContext", 1, prim_hasContext);
source-only deployment). This primop marks the string context so source-only deployment). This primop marks the string context so
that builtins.derivation adds the path to drv.inputSrcs rather than that builtins.derivation adds the path to drv.inputSrcs rather than
drv.inputDrvs. */ drv.inputDrvs. */
static void prim_unsafeDiscardOutputDependency(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_unsafeDiscardOutputDependency(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
PathSet context; PathSet context;
auto s = state.coerceToString(pos, *args[0], context); auto s = state.coerceToString(pos, *args[0], context);
@ -65,7 +65,7 @@ static RegisterPrimOp primop_unsafeDiscardOutputDependency("__unsafeDiscardOutpu
Note that for a given path any combination of the above attributes Note that for a given path any combination of the above attributes
may be present. may be present.
*/ */
static void prim_getContext(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_getContext(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
struct ContextInfo { struct ContextInfo {
bool path = false; bool path = false;
@ -134,7 +134,7 @@ static RegisterPrimOp primop_getContext("__getContext", 1, prim_getContext);
See the commentary above unsafeGetContext for details of the See the commentary above unsafeGetContext for details of the
context representation. context representation.
*/ */
static void prim_appendContext(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
PathSet context; PathSet context;
auto orig = state.forceString(*args[0], context, pos); auto orig = state.forceString(*args[0], context, pos);
@ -147,24 +147,24 @@ static void prim_appendContext(EvalState & state, const Pos & pos, Value * * arg
if (!state.store->isStorePath(i.name)) if (!state.store->isStorePath(i.name))
throw EvalError({ throw EvalError({
.msg = hintfmt("Context key '%s' is not a store path", i.name), .msg = hintfmt("Context key '%s' is not a store path", i.name),
.errPos = *i.pos .errPos = state.positions[i.pos]
}); });
if (!settings.readOnlyMode) if (!settings.readOnlyMode)
state.store->ensurePath(state.store->parseStorePath(i.name)); state.store->ensurePath(state.store->parseStorePath(i.name));
state.forceAttrs(*i.value, *i.pos); state.forceAttrs(*i.value, i.pos);
auto iter = i.value->attrs->find(sPath); auto iter = i.value->attrs->find(sPath);
if (iter != i.value->attrs->end()) { if (iter != i.value->attrs->end()) {
if (state.forceBool(*iter->value, *iter->pos)) if (state.forceBool(*iter->value, iter->pos))
context.insert(i.name); context.insert(i.name);
} }
iter = i.value->attrs->find(sAllOutputs); iter = i.value->attrs->find(sAllOutputs);
if (iter != i.value->attrs->end()) { if (iter != i.value->attrs->end()) {
if (state.forceBool(*iter->value, *iter->pos)) { if (state.forceBool(*iter->value, iter->pos)) {
if (!isDerivation(i.name)) { if (!isDerivation(i.name)) {
throw EvalError({ throw EvalError({
.msg = hintfmt("Tried to add all-outputs context of %s, which is not a derivation, to a string", i.name), .msg = hintfmt("Tried to add all-outputs context of %s, which is not a derivation, to a string", i.name),
.errPos = *i.pos .errPos = state.positions[i.pos]
}); });
} }
context.insert("=" + std::string(i.name)); context.insert("=" + std::string(i.name));
@ -173,15 +173,15 @@ static void prim_appendContext(EvalState & state, const Pos & pos, Value * * arg
iter = i.value->attrs->find(state.sOutputs); iter = i.value->attrs->find(state.sOutputs);
if (iter != i.value->attrs->end()) { if (iter != i.value->attrs->end()) {
state.forceList(*iter->value, *iter->pos); state.forceList(*iter->value, iter->pos);
if (iter->value->listSize() && !isDerivation(i.name)) { if (iter->value->listSize() && !isDerivation(i.name)) {
throw EvalError({ throw EvalError({
.msg = hintfmt("Tried to add derivation output context of %s, which is not a derivation, to a string", i.name), .msg = hintfmt("Tried to add derivation output context of %s, which is not a derivation, to a string", i.name),
.errPos = *i.pos .errPos = state.positions[i.pos]
}); });
} }
for (auto elem : iter->value->listItems()) { for (auto elem : iter->value->listItems()) {
auto name = state.forceStringNoCtx(*elem, *iter->pos); auto name = state.forceStringNoCtx(*elem, iter->pos);
context.insert(concatStrings("!", name, "!", i.name)); context.insert(concatStrings("!", name, "!", i.name));
} }
} }

View file

@ -5,7 +5,7 @@
namespace nix { namespace nix {
static void prim_fetchClosure(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_fetchClosure(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
state.forceAttrs(*args[0], pos); state.forceAttrs(*args[0], pos);
@ -17,38 +17,38 @@ static void prim_fetchClosure(EvalState & state, const Pos & pos, Value * * args
for (auto & attr : *args[0]->attrs) { for (auto & attr : *args[0]->attrs) {
if (attr.name == "fromPath") { if (attr.name == "fromPath") {
PathSet context; PathSet context;
fromPath = state.coerceToStorePath(*attr.pos, *attr.value, context); fromPath = state.coerceToStorePath(attr.pos, *attr.value, context);
} }
else if (attr.name == "toPath") { else if (attr.name == "toPath") {
state.forceValue(*attr.value, *attr.pos); state.forceValue(*attr.value, attr.pos);
toCA = true; toCA = true;
if (attr.value->type() != nString || attr.value->string.s != std::string("")) { if (attr.value->type() != nString || attr.value->string.s != std::string("")) {
PathSet context; PathSet context;
toPath = state.coerceToStorePath(*attr.pos, *attr.value, context); toPath = state.coerceToStorePath(attr.pos, *attr.value, context);
} }
} }
else if (attr.name == "fromStore") else if (attr.name == "fromStore")
fromStoreUrl = state.forceStringNoCtx(*attr.value, *attr.pos); fromStoreUrl = state.forceStringNoCtx(*attr.value, attr.pos);
else else
throw Error({ throw Error({
.msg = hintfmt("attribute '%s' isn't supported in call to 'fetchClosure'", attr.name), .msg = hintfmt("attribute '%s' isn't supported in call to 'fetchClosure'", attr.name),
.errPos = pos .errPos = state.positions[pos]
}); });
} }
if (!fromPath) if (!fromPath)
throw Error({ throw Error({
.msg = hintfmt("attribute '%s' is missing in call to 'fetchClosure'", "fromPath"), .msg = hintfmt("attribute '%s' is missing in call to 'fetchClosure'", "fromPath"),
.errPos = pos .errPos = state.positions[pos]
}); });
if (!fromStoreUrl) if (!fromStoreUrl)
throw Error({ throw Error({
.msg = hintfmt("attribute '%s' is missing in call to 'fetchClosure'", "fromStore"), .msg = hintfmt("attribute '%s' is missing in call to 'fetchClosure'", "fromStore"),
.errPos = pos .errPos = state.positions[pos]
}); });
auto parsedURL = parseURL(*fromStoreUrl); auto parsedURL = parseURL(*fromStoreUrl);
@ -58,13 +58,13 @@ static void prim_fetchClosure(EvalState & state, const Pos & pos, Value * * args
!(getEnv("_NIX_IN_TEST").has_value() && parsedURL.scheme == "file")) !(getEnv("_NIX_IN_TEST").has_value() && parsedURL.scheme == "file"))
throw Error({ throw Error({
.msg = hintfmt("'fetchClosure' only supports http:// and https:// stores"), .msg = hintfmt("'fetchClosure' only supports http:// and https:// stores"),
.errPos = pos .errPos = state.positions[pos]
}); });
if (!parsedURL.query.empty()) if (!parsedURL.query.empty())
throw Error({ throw Error({
.msg = hintfmt("'fetchClosure' does not support URL query parameters (in '%s')", *fromStoreUrl), .msg = hintfmt("'fetchClosure' does not support URL query parameters (in '%s')", *fromStoreUrl),
.errPos = pos .errPos = state.positions[pos]
}); });
auto fromStore = openStore(parsedURL.to_string()); auto fromStore = openStore(parsedURL.to_string());
@ -80,7 +80,7 @@ static void prim_fetchClosure(EvalState & state, const Pos & pos, Value * * args
state.store->printStorePath(*fromPath), state.store->printStorePath(*fromPath),
state.store->printStorePath(i->second), state.store->printStorePath(i->second),
state.store->printStorePath(*toPath)), state.store->printStorePath(*toPath)),
.errPos = pos .errPos = state.positions[pos]
}); });
if (!toPath) if (!toPath)
throw Error({ throw Error({
@ -89,7 +89,7 @@ static void prim_fetchClosure(EvalState & state, const Pos & pos, Value * * args
"please set this in the 'toPath' attribute passed to 'fetchClosure'", "please set this in the 'toPath' attribute passed to 'fetchClosure'",
state.store->printStorePath(*fromPath), state.store->printStorePath(*fromPath),
state.store->printStorePath(i->second)), state.store->printStorePath(i->second)),
.errPos = pos .errPos = state.positions[pos]
}); });
} }
} else { } else {
@ -105,7 +105,7 @@ static void prim_fetchClosure(EvalState & state, const Pos & pos, Value * * args
throw Error({ throw Error({
.msg = hintfmt("in pure mode, 'fetchClosure' requires a content-addressed path, which '%s' isn't", .msg = hintfmt("in pure mode, 'fetchClosure' requires a content-addressed path, which '%s' isn't",
state.store->printStorePath(*toPath)), state.store->printStorePath(*toPath)),
.errPos = pos .errPos = state.positions[pos]
}); });
} }

View file

@ -7,7 +7,7 @@
namespace nix { namespace nix {
static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
std::string url; std::string url;
std::optional<Hash> rev; std::optional<Hash> rev;
@ -24,29 +24,29 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar
for (auto & attr : *args[0]->attrs) { for (auto & attr : *args[0]->attrs) {
std::string_view n(attr.name); std::string_view n(attr.name);
if (n == "url") if (n == "url")
url = state.coerceToString(*attr.pos, *attr.value, context, false, false).toOwned(); url = state.coerceToString(attr.pos, *attr.value, context, false, false).toOwned();
else if (n == "rev") { else if (n == "rev") {
// Ugly: unlike fetchGit, here the "rev" attribute can // Ugly: unlike fetchGit, here the "rev" attribute can
// be both a revision or a branch/tag name. // be both a revision or a branch/tag name.
auto value = state.forceStringNoCtx(*attr.value, *attr.pos); auto value = state.forceStringNoCtx(*attr.value, attr.pos);
if (std::regex_match(value.begin(), value.end(), revRegex)) if (std::regex_match(value.begin(), value.end(), revRegex))
rev = Hash::parseAny(value, htSHA1); rev = Hash::parseAny(value, htSHA1);
else else
ref = value; ref = value;
} }
else if (n == "name") else if (n == "name")
name = state.forceStringNoCtx(*attr.value, *attr.pos); name = state.forceStringNoCtx(*attr.value, attr.pos);
else else
throw EvalError({ throw EvalError({
.msg = hintfmt("unsupported argument '%s' to 'fetchMercurial'", attr.name), .msg = hintfmt("unsupported argument '%s' to 'fetchMercurial'", attr.name),
.errPos = *attr.pos .errPos = state.positions[attr.pos]
}); });
} }
if (url.empty()) if (url.empty())
throw EvalError({ throw EvalError({
.msg = hintfmt("'url' argument required"), .msg = hintfmt("'url' argument required"),
.errPos = pos .errPos = state.positions[pos]
}); });
} else } else

View file

@ -90,7 +90,7 @@ struct FetchTreeParams {
static void fetchTree( static void fetchTree(
EvalState & state, EvalState & state,
const Pos & pos, const PosIdx pos,
Value * * args, Value * * args,
Value & v, Value & v,
std::optional<std::string> type, std::optional<std::string> type,
@ -110,22 +110,22 @@ static void fetchTree(
if (type) if (type)
throw Error({ throw Error({
.msg = hintfmt("unexpected attribute 'type'"), .msg = hintfmt("unexpected attribute 'type'"),
.errPos = pos .errPos = state.positions[pos]
}); });
type = state.forceStringNoCtx(*aType->value, *aType->pos); type = state.forceStringNoCtx(*aType->value, aType->pos);
} else if (!type) } else if (!type)
throw Error({ throw Error({
.msg = hintfmt("attribute 'type' is missing in call to 'fetchTree'"), .msg = hintfmt("attribute 'type' is missing in call to 'fetchTree'"),
.errPos = pos .errPos = state.positions[pos]
}); });
attrs.emplace("type", type.value()); attrs.emplace("type", type.value());
for (auto & attr : *args[0]->attrs) { for (auto & attr : *args[0]->attrs) {
if (attr.name == state.sType) continue; if (attr.name == state.sType) continue;
state.forceValue(*attr.value, *attr.pos); state.forceValue(*attr.value, attr.pos);
if (attr.value->type() == nPath || attr.value->type() == nString) { if (attr.value->type() == nPath || attr.value->type() == nString) {
auto s = state.coerceToString(*attr.pos, *attr.value, context, false, false).toOwned(); auto s = state.coerceToString(attr.pos, *attr.value, context, false, false).toOwned();
attrs.emplace(attr.name, attrs.emplace(attr.name,
attr.name == "url" attr.name == "url"
? type == "git" ? type == "git"
@ -146,7 +146,7 @@ static void fetchTree(
if (auto nameIter = attrs.find("name"); nameIter != attrs.end()) if (auto nameIter = attrs.find("name"); nameIter != attrs.end())
throw Error({ throw Error({
.msg = hintfmt("attribute 'name' isn't supported in call to 'fetchTree'"), .msg = hintfmt("attribute 'name' isn't supported in call to 'fetchTree'"),
.errPos = pos .errPos = state.positions[pos]
}); });
input = fetchers::Input::fromAttrs(std::move(attrs)); input = fetchers::Input::fromAttrs(std::move(attrs));
@ -167,7 +167,7 @@ static void fetchTree(
input = lookupInRegistries(state.store, input).first; input = lookupInRegistries(state.store, input).first;
if (evalSettings.pureEval && !input.isLocked()) if (evalSettings.pureEval && !input.isLocked())
throw Error("in pure evaluation mode, 'fetchTree' requires a locked input, at %s", pos); throw Error("in pure evaluation mode, 'fetchTree' requires a locked input, at %s", state.positions[pos]);
auto [tree, input2] = input.fetch(state.store); auto [tree, input2] = input.fetch(state.store);
@ -176,7 +176,7 @@ static void fetchTree(
emitTreeAttrs(state, tree, input2, v, params.emptyRevFallback, false); emitTreeAttrs(state, tree, input2, v, params.emptyRevFallback, false);
} }
static void prim_fetchTree(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_fetchTree(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
settings.requireExperimentalFeature(Xp::Flakes); settings.requireExperimentalFeature(Xp::Flakes);
fetchTree(state, pos, args, v, std::nullopt, FetchTreeParams { .allowNameArgument = false }); fetchTree(state, pos, args, v, std::nullopt, FetchTreeParams { .allowNameArgument = false });
@ -185,7 +185,7 @@ static void prim_fetchTree(EvalState & state, const Pos & pos, Value * * args, V
// FIXME: document // FIXME: document
static RegisterPrimOp primop_fetchTree("fetchTree", 1, prim_fetchTree); static RegisterPrimOp primop_fetchTree("fetchTree", 1, prim_fetchTree);
static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v, static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v,
const std::string & who, bool unpack, std::string name) const std::string & who, bool unpack, std::string name)
{ {
std::optional<std::string> url; std::optional<std::string> url;
@ -200,22 +200,22 @@ static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
for (auto & attr : *args[0]->attrs) { for (auto & attr : *args[0]->attrs) {
std::string n(attr.name); std::string n(attr.name);
if (n == "url") if (n == "url")
url = state.forceStringNoCtx(*attr.value, *attr.pos); url = state.forceStringNoCtx(*attr.value, attr.pos);
else if (n == "sha256") else if (n == "sha256")
expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, *attr.pos), htSHA256); expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, attr.pos), htSHA256);
else if (n == "name") else if (n == "name")
name = state.forceStringNoCtx(*attr.value, *attr.pos); name = state.forceStringNoCtx(*attr.value, attr.pos);
else else
throw EvalError({ throw EvalError({
.msg = hintfmt("unsupported argument '%s' to '%s'", attr.name, who), .msg = hintfmt("unsupported argument '%s' to '%s'", attr.name, who),
.errPos = *attr.pos .errPos = state.positions[attr.pos]
}); });
} }
if (!url) if (!url)
throw EvalError({ throw EvalError({
.msg = hintfmt("'url' argument required"), .msg = hintfmt("'url' argument required"),
.errPos = pos .errPos = state.positions[pos]
}); });
} else } else
url = state.forceStringNoCtx(*args[0], pos); url = state.forceStringNoCtx(*args[0], pos);
@ -262,7 +262,7 @@ static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
state.allowAndSetStorePathString(storePath, v); state.allowAndSetStorePathString(storePath, v);
} }
static void prim_fetchurl(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_fetchurl(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
fetch(state, pos, args, v, "fetchurl", false, ""); fetch(state, pos, args, v, "fetchurl", false, "");
} }
@ -278,7 +278,7 @@ static RegisterPrimOp primop_fetchurl({
.fun = prim_fetchurl, .fun = prim_fetchurl,
}); });
static void prim_fetchTarball(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_fetchTarball(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
fetch(state, pos, args, v, "fetchTarball", true, "source"); fetch(state, pos, args, v, "fetchTarball", true, "source");
} }
@ -329,7 +329,7 @@ static RegisterPrimOp primop_fetchTarball({
.fun = prim_fetchTarball, .fun = prim_fetchTarball,
}); });
static void prim_fetchGit(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_fetchGit(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
fetchTree(state, pos, args, v, "git", FetchTreeParams { .emptyRevFallback = true, .allowNameArgument = true }); fetchTree(state, pos, args, v, "git", FetchTreeParams { .emptyRevFallback = true, .allowNameArgument = true });
} }

View file

@ -5,7 +5,7 @@
namespace nix { namespace nix {
static void prim_fromTOML(EvalState & state, const Pos & pos, Value * * args, Value & val) static void prim_fromTOML(EvalState & state, const PosIdx pos, Value * * args, Value & val)
{ {
auto toml = state.forceStringNoCtx(*args[0], pos); auto toml = state.forceStringNoCtx(*args[0], pos);
@ -73,7 +73,7 @@ static void prim_fromTOML(EvalState & state, const Pos & pos, Value * * args, Va
} catch (std::exception & e) { // TODO: toml::syntax_error } catch (std::exception & e) { // TODO: toml::syntax_error
throw EvalError({ throw EvalError({
.msg = hintfmt("while parsing a TOML string: %s", e.what()), .msg = hintfmt("while parsing a TOML string: %s", e.what()),
.errPos = pos .errPos = state.positions[pos]
}); });
} }
} }

View file

@ -10,7 +10,7 @@
namespace nix { namespace nix {
void printValueAsJSON(EvalState & state, bool strict, void printValueAsJSON(EvalState & state, bool strict,
Value & v, const Pos & pos, JSONPlaceholder & out, PathSet & context) Value & v, const PosIdx pos, JSONPlaceholder & out, PathSet & context)
{ {
checkInterrupt(); checkInterrupt();
@ -54,10 +54,10 @@ void printValueAsJSON(EvalState & state, bool strict,
for (auto & j : names) { for (auto & j : names) {
Attr & a(*v.attrs->find(state.symbols.create(j))); Attr & a(*v.attrs->find(state.symbols.create(j)));
auto placeholder(obj.placeholder(j)); auto placeholder(obj.placeholder(j));
printValueAsJSON(state, strict, *a.value, *a.pos, placeholder, context); printValueAsJSON(state, strict, *a.value, a.pos, placeholder, context);
} }
} else } else
printValueAsJSON(state, strict, *i->value, *i->pos, out, context); printValueAsJSON(state, strict, *i->value, i->pos, out, context);
break; break;
} }
@ -82,15 +82,15 @@ void printValueAsJSON(EvalState & state, bool strict,
case nFunction: case nFunction:
auto e = TypeError({ auto e = TypeError({
.msg = hintfmt("cannot convert %1% to JSON", showType(v)), .msg = hintfmt("cannot convert %1% to JSON", showType(v)),
.errPos = v.determinePos(pos) .errPos = state.positions[v.determinePos(pos)]
}); });
e.addTrace(pos, hintfmt("message for the trace")); e.addTrace(state.positions[pos], hintfmt("message for the trace"));
throw e; throw e;
} }
} }
void printValueAsJSON(EvalState & state, bool strict, void printValueAsJSON(EvalState & state, bool strict,
Value & v, const Pos & pos, std::ostream & str, PathSet & context) Value & v, const PosIdx pos, std::ostream & str, PathSet & context)
{ {
JSONPlaceholder out(str); JSONPlaceholder out(str);
printValueAsJSON(state, strict, v, pos, out, context); printValueAsJSON(state, strict, v, pos, out, context);

View file

@ -11,9 +11,9 @@ namespace nix {
class JSONPlaceholder; class JSONPlaceholder;
void printValueAsJSON(EvalState & state, bool strict, void printValueAsJSON(EvalState & state, bool strict,
Value & v, const Pos & pos, JSONPlaceholder & out, PathSet & context); Value & v, const PosIdx pos, JSONPlaceholder & out, PathSet & context);
void printValueAsJSON(EvalState & state, bool strict, void printValueAsJSON(EvalState & state, bool strict,
Value & v, const Pos & pos, std::ostream & str, PathSet & context); Value & v, const PosIdx pos, std::ostream & str, PathSet & context);
} }

View file

@ -19,7 +19,7 @@ static XMLAttrs singletonAttrs(const std::string & name, const std::string & val
static void printValueAsXML(EvalState & state, bool strict, bool location, static void printValueAsXML(EvalState & state, bool strict, bool location,
Value & v, XMLWriter & doc, PathSet & context, PathSet & drvsSeen, Value & v, XMLWriter & doc, PathSet & context, PathSet & drvsSeen,
const Pos & pos); const PosIdx pos);
static void posToXML(XMLAttrs & xmlAttrs, const Pos & pos) static void posToXML(XMLAttrs & xmlAttrs, const Pos & pos)
@ -43,18 +43,18 @@ static void showAttrs(EvalState & state, bool strict, bool location,
XMLAttrs xmlAttrs; XMLAttrs xmlAttrs;
xmlAttrs["name"] = i; xmlAttrs["name"] = i;
if (location && a.pos != ptr(&noPos)) posToXML(xmlAttrs, *a.pos); if (location && a.pos) posToXML(xmlAttrs, state.positions[a.pos]);
XMLOpenElement _(doc, "attr", xmlAttrs); XMLOpenElement _(doc, "attr", xmlAttrs);
printValueAsXML(state, strict, location, printValueAsXML(state, strict, location,
*a.value, doc, context, drvsSeen, *a.pos); *a.value, doc, context, drvsSeen, a.pos);
} }
} }
static void printValueAsXML(EvalState & state, bool strict, bool location, static void printValueAsXML(EvalState & state, bool strict, bool location,
Value & v, XMLWriter & doc, PathSet & context, PathSet & drvsSeen, Value & v, XMLWriter & doc, PathSet & context, PathSet & drvsSeen,
const Pos & pos) const PosIdx pos)
{ {
checkInterrupt(); checkInterrupt();
@ -93,14 +93,14 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
Path drvPath; Path drvPath;
a = v.attrs->find(state.sDrvPath); a = v.attrs->find(state.sDrvPath);
if (a != v.attrs->end()) { if (a != v.attrs->end()) {
if (strict) state.forceValue(*a->value, *a->pos); if (strict) state.forceValue(*a->value, a->pos);
if (a->value->type() == nString) if (a->value->type() == nString)
xmlAttrs["drvPath"] = drvPath = a->value->string.s; xmlAttrs["drvPath"] = drvPath = a->value->string.s;
} }
a = v.attrs->find(state.sOutPath); a = v.attrs->find(state.sOutPath);
if (a != v.attrs->end()) { if (a != v.attrs->end()) {
if (strict) state.forceValue(*a->value, *a->pos); if (strict) state.forceValue(*a->value, a->pos);
if (a->value->type() == nString) if (a->value->type() == nString)
xmlAttrs["outPath"] = a->value->string.s; xmlAttrs["outPath"] = a->value->string.s;
} }
@ -134,7 +134,7 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
break; break;
} }
XMLAttrs xmlAttrs; XMLAttrs xmlAttrs;
if (location) posToXML(xmlAttrs, v.lambda.fun->pos); if (location) posToXML(xmlAttrs, state.positions[v.lambda.fun->pos]);
XMLOpenElement _(doc, "function", xmlAttrs); XMLOpenElement _(doc, "function", xmlAttrs);
if (v.lambda.fun->hasFormals()) { if (v.lambda.fun->hasFormals()) {
@ -166,14 +166,14 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
void ExternalValueBase::printValueAsXML(EvalState & state, bool strict, void ExternalValueBase::printValueAsXML(EvalState & state, bool strict,
bool location, XMLWriter & doc, PathSet & context, PathSet & drvsSeen, bool location, XMLWriter & doc, PathSet & context, PathSet & drvsSeen,
const Pos & pos) const const PosIdx pos) const
{ {
doc.writeEmptyElement("unevaluated"); doc.writeEmptyElement("unevaluated");
} }
void printValueAsXML(EvalState & state, bool strict, bool location, void printValueAsXML(EvalState & state, bool strict, bool location,
Value & v, std::ostream & out, PathSet & context, const Pos & pos) Value & v, std::ostream & out, PathSet & context, const PosIdx pos)
{ {
XMLWriter doc(true, out); XMLWriter doc(true, out);
XMLOpenElement root(doc, "expr"); XMLOpenElement root(doc, "expr");

View file

@ -9,6 +9,6 @@
namespace nix { namespace nix {
void printValueAsXML(EvalState & state, bool strict, bool location, void printValueAsXML(EvalState & state, bool strict, bool location,
Value & v, std::ostream & out, PathSet & context, const Pos & pos); Value & v, std::ostream & out, PathSet & context, const PosIdx pos);
} }

View file

@ -56,6 +56,7 @@ struct Expr;
struct ExprLambda; struct ExprLambda;
struct PrimOp; struct PrimOp;
class Symbol; class Symbol;
class PosIdx;
struct Pos; struct Pos;
class StorePath; class StorePath;
class Store; class Store;
@ -103,7 +104,7 @@ class ExternalValueBase
/* Print the value as XML. Defaults to unevaluated */ /* Print the value as XML. Defaults to unevaluated */
virtual void printValueAsXML(EvalState & state, bool strict, bool location, virtual void printValueAsXML(EvalState & state, bool strict, bool location,
XMLWriter & doc, PathSet & context, PathSet & drvsSeen, XMLWriter & doc, PathSet & context, PathSet & drvsSeen,
const Pos & pos) const; const PosIdx pos) const;
virtual ~ExternalValueBase() virtual ~ExternalValueBase()
{ {
@ -368,7 +369,7 @@ public:
return internalType == tList1 ? 1 : internalType == tList2 ? 2 : bigList.size; return internalType == tList1 ? 1 : internalType == tList2 ? 2 : bigList.size;
} }
Pos determinePos(const Pos & pos) const; PosIdx determinePos(const PosIdx pos) const;
/* Check whether forcing this value requires a trivial amount of /* Check whether forcing this value requires a trivial amount of
computation. In particular, function applications are computation. In particular, function applications are

View file

@ -5,6 +5,7 @@
#include <list> #include <list>
#include <set> #include <set>
#include <string> #include <string>
#include <limits>
#include <map> #include <map>
#include <variant> #include <variant>
#include <vector> #include <vector>
@ -102,4 +103,55 @@ public:
Ptr operator->() const { return Ptr(**this); } Ptr operator->() const { return Ptr(**this); }
}; };
/* Provides an indexable container like vector<> with memory overhead
guarantees like list<> by allocating storage in chunks of ChunkSize
elements instead of using a contiguous memory allocation like vector<>
does. Not using a single vector that is resized reduces memory overhead
on large data sets by on average (growth factor)/2, mostly
eliminates copies within the vector during resizing, and provides stable
references to its elements. */
template<typename T, size_t ChunkSize>
class ChunkedVector {
private:
uint32_t size_ = 0;
std::vector<std::vector<T>> chunks;
/* keep this out of the ::add hot path */
[[gnu::noinline]]
auto & addChunk()
{
if (size_ >= std::numeric_limits<uint32_t>::max() - ChunkSize)
abort();
chunks.emplace_back();
chunks.back().reserve(ChunkSize);
return chunks.back();
}
public:
ChunkedVector(uint32_t reserve)
{
chunks.reserve(reserve);
addChunk();
}
uint32_t size() const { return size_; }
std::pair<T &, uint32_t> add(T value)
{
const auto idx = size_++;
auto & chunk = [&] () -> auto & {
if (auto & back = chunks.back(); back.size() < ChunkSize)
return back;
return addChunk();
}();
auto & result = chunk.emplace_back(std::move(value));
return {result, idx};
}
const T & operator[](uint32_t idx) const
{
return chunks[idx / ChunkSize][idx % ChunkSize];
}
};
} }

View file

@ -134,9 +134,9 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
state.forceValue(topLevel, [&]() { return topLevel.determinePos(noPos); }); state.forceValue(topLevel, [&]() { return topLevel.determinePos(noPos); });
PathSet context; PathSet context;
Attr & aDrvPath(*topLevel.attrs->find(state.sDrvPath)); Attr & aDrvPath(*topLevel.attrs->find(state.sDrvPath));
auto topLevelDrv = state.coerceToStorePath(*aDrvPath.pos, *aDrvPath.value, context); auto topLevelDrv = state.coerceToStorePath(aDrvPath.pos, *aDrvPath.value, context);
Attr & aOutPath(*topLevel.attrs->find(state.sOutPath)); Attr & aOutPath(*topLevel.attrs->find(state.sOutPath));
auto topLevelOut = state.coerceToStorePath(*aOutPath.pos, *aOutPath.value, context); auto topLevelOut = state.coerceToStorePath(aOutPath.pos, *aOutPath.value, context);
/* Realise the resulting store expression. */ /* Realise the resulting store expression. */
debug("building user environment"); debug("building user environment");

View file

@ -97,13 +97,13 @@ struct CmdBundle : InstallableCommand
throw Error("the bundler '%s' does not produce a derivation", bundler.what()); throw Error("the bundler '%s' does not produce a derivation", bundler.what());
PathSet context2; PathSet context2;
auto drvPath = evalState->coerceToStorePath(*attr1->pos, *attr1->value, context2); auto drvPath = evalState->coerceToStorePath(attr1->pos, *attr1->value, context2);
auto attr2 = vRes->attrs->get(evalState->sOutPath); auto attr2 = vRes->attrs->get(evalState->sOutPath);
if (!attr2) if (!attr2)
throw Error("the bundler '%s' does not produce a derivation", bundler.what()); throw Error("the bundler '%s' does not produce a derivation", bundler.what());
auto outPath = evalState->coerceToStorePath(*attr2->pos, *attr2->value, context2); auto outPath = evalState->coerceToStorePath(attr2->pos, *attr2->value, context2);
store->buildPaths({ DerivedPath::Built { drvPath } }); store->buildPaths({ DerivedPath::Built { drvPath } });
@ -113,7 +113,7 @@ struct CmdBundle : InstallableCommand
auto * attr = vRes->attrs->get(evalState->sName); auto * attr = vRes->attrs->get(evalState->sName);
if (!attr) if (!attr)
throw Error("attribute 'name' missing"); throw Error("attribute 'name' missing");
outLink = evalState->forceStringNoCtx(*attr->value, *attr->pos); outLink = evalState->forceStringNoCtx(*attr->value, attr->pos);
} }
// TODO: will crash if not a localFSStore? // TODO: will crash if not a localFSStore?

View file

@ -28,9 +28,9 @@ struct CmdEdit : InstallableCommand
{ {
auto state = getEvalState(); auto state = getEvalState();
auto [v, pos] = installable->toValue(*state);
const auto [file, line] = [&] { const auto [file, line] = [&] {
auto [v, pos] = installable->toValue(*state);
try { try {
return findPackageFilename(*state, *v, installable->what()); return findPackageFilename(*state, *v, installable->what());
} catch (NoPositionInfo &) { } catch (NoPositionInfo &) {

View file

@ -77,9 +77,9 @@ struct CmdEval : MixJSON, InstallableCommand
if (pathExists(*writeTo)) if (pathExists(*writeTo))
throw Error("path '%s' already exists", *writeTo); throw Error("path '%s' already exists", *writeTo);
std::function<void(Value & v, const Pos & pos, const Path & path)> recurse; std::function<void(Value & v, const PosIdx pos, const Path & path)> recurse;
recurse = [&](Value & v, const Pos & pos, const Path & path) recurse = [&](Value & v, const PosIdx pos, const Path & path)
{ {
state->forceValue(v, pos); state->forceValue(v, pos);
if (v.type() == nString) if (v.type() == nString)
@ -92,14 +92,16 @@ struct CmdEval : MixJSON, InstallableCommand
try { try {
if (attr.name == "." || attr.name == "..") if (attr.name == "." || attr.name == "..")
throw Error("invalid file name '%s'", attr.name); throw Error("invalid file name '%s'", attr.name);
recurse(*attr.value, *attr.pos, path + "/" + std::string(attr.name)); recurse(*attr.value, attr.pos, path + "/" + std::string(attr.name));
} catch (Error & e) { } catch (Error & e) {
e.addTrace(*attr.pos, hintfmt("while evaluating the attribute '%s'", attr.name)); e.addTrace(
state->positions[attr.pos],
hintfmt("while evaluating the attribute '%s'", attr.name));
throw; throw;
} }
} }
else else
throw TypeError("value at '%s' is not a string or an attribute set", pos); throw TypeError("value at '%s' is not a string or an attribute set", state->positions[pos]);
}; };
recurse(*v, pos, *writeTo); recurse(*v, pos, *writeTo);

View file

@ -123,7 +123,7 @@ struct CmdFlakeLock : FlakeCommand
}; };
static void enumerateOutputs(EvalState & state, Value & vFlake, static void enumerateOutputs(EvalState & state, Value & vFlake,
std::function<void(const std::string & name, Value & vProvide, const Pos & pos)> callback) std::function<void(const std::string & name, Value & vProvide, const PosIdx pos)> callback)
{ {
auto pos = vFlake.determinePos(noPos); auto pos = vFlake.determinePos(noPos);
state.forceAttrs(vFlake, pos); state.forceAttrs(vFlake, pos);
@ -139,11 +139,11 @@ static void enumerateOutputs(EvalState & state, Value & vFlake,
else. This way we can disable IFD for hydraJobs and then enable else. This way we can disable IFD for hydraJobs and then enable
it for other outputs. */ it for other outputs. */
if (auto attr = aOutputs->value->attrs->get(sHydraJobs)) if (auto attr = aOutputs->value->attrs->get(sHydraJobs))
callback(attr->name, *attr->value, *attr->pos); callback(attr->name, *attr->value, attr->pos);
for (auto & attr : *aOutputs->value->attrs) { for (auto & attr : *aOutputs->value->attrs) {
if (attr.name != sHydraJobs) if (attr.name != sHydraJobs)
callback(attr.name, *attr.value, *attr.pos); callback(attr.name, *attr.value, attr.pos);
} }
} }
@ -315,13 +315,17 @@ struct CmdFlakeCheck : FlakeCommand
// FIXME: rewrite to use EvalCache. // FIXME: rewrite to use EvalCache.
auto checkSystemName = [&](const std::string & system, const Pos & pos) { auto resolve = [&] (PosIdx p) {
// FIXME: what's the format of "system"? return state->positions[p];
if (system.find('-') == std::string::npos)
reportError(Error("'%s' is not a valid system type, at %s", system, pos));
}; };
auto checkDerivation = [&](const std::string & attrPath, Value & v, const Pos & pos) -> std::optional<StorePath> { auto checkSystemName = [&](const std::string & system, const PosIdx pos) {
// FIXME: what's the format of "system"?
if (system.find('-') == std::string::npos)
reportError(Error("'%s' is not a valid system type, at %s", system, resolve(pos)));
};
auto checkDerivation = [&](const std::string & attrPath, Value & v, const PosIdx pos) -> std::optional<StorePath> {
try { try {
auto drvInfo = getDerivation(*state, v, false); auto drvInfo = getDerivation(*state, v, false);
if (!drvInfo) if (!drvInfo)
@ -329,7 +333,7 @@ struct CmdFlakeCheck : FlakeCommand
// FIXME: check meta attributes // FIXME: check meta attributes
return drvInfo->queryDrvPath(); return drvInfo->queryDrvPath();
} catch (Error & e) { } catch (Error & e) {
e.addTrace(pos, hintfmt("while checking the derivation '%s'", attrPath)); e.addTrace(resolve(pos), hintfmt("while checking the derivation '%s'", attrPath));
reportError(e); reportError(e);
} }
return std::nullopt; return std::nullopt;
@ -337,7 +341,7 @@ struct CmdFlakeCheck : FlakeCommand
std::vector<DerivedPath> drvPaths; std::vector<DerivedPath> drvPaths;
auto checkApp = [&](const std::string & attrPath, Value & v, const Pos & pos) { auto checkApp = [&](const std::string & attrPath, Value & v, const PosIdx pos) {
try { try {
#if 0 #if 0
// FIXME // FIXME
@ -348,12 +352,12 @@ struct CmdFlakeCheck : FlakeCommand
} }
#endif #endif
} catch (Error & e) { } catch (Error & e) {
e.addTrace(pos, hintfmt("while checking the app definition '%s'", attrPath)); e.addTrace(resolve(pos), hintfmt("while checking the app definition '%s'", attrPath));
reportError(e); reportError(e);
} }
}; };
auto checkOverlay = [&](const std::string & attrPath, Value & v, const Pos & pos) { auto checkOverlay = [&](const std::string & attrPath, Value & v, const PosIdx pos) {
try { try {
state->forceValue(v, pos); state->forceValue(v, pos);
if (!v.isLambda() if (!v.isLambda()
@ -368,12 +372,12 @@ struct CmdFlakeCheck : FlakeCommand
// FIXME: if we have a 'nixpkgs' input, use it to // FIXME: if we have a 'nixpkgs' input, use it to
// evaluate the overlay. // evaluate the overlay.
} catch (Error & e) { } catch (Error & e) {
e.addTrace(pos, hintfmt("while checking the overlay '%s'", attrPath)); e.addTrace(resolve(pos), hintfmt("while checking the overlay '%s'", attrPath));
reportError(e); reportError(e);
} }
}; };
auto checkModule = [&](const std::string & attrPath, Value & v, const Pos & pos) { auto checkModule = [&](const std::string & attrPath, Value & v, const PosIdx pos) {
try { try {
state->forceValue(v, pos); state->forceValue(v, pos);
if (v.isLambda()) { if (v.isLambda()) {
@ -382,9 +386,11 @@ struct CmdFlakeCheck : FlakeCommand
} else if (v.type() == nAttrs) { } else if (v.type() == nAttrs) {
for (auto & attr : *v.attrs) for (auto & attr : *v.attrs)
try { try {
state->forceValue(*attr.value, *attr.pos); state->forceValue(*attr.value, attr.pos);
} catch (Error & e) { } catch (Error & e) {
e.addTrace(*attr.pos, hintfmt("while evaluating the option '%s'", attr.name)); e.addTrace(
state->positions[attr.pos],
hintfmt("while evaluating the option '%s'", attr.name));
throw; throw;
} }
} else } else
@ -392,14 +398,14 @@ struct CmdFlakeCheck : FlakeCommand
// FIXME: if we have a 'nixpkgs' input, use it to // FIXME: if we have a 'nixpkgs' input, use it to
// check the module. // check the module.
} catch (Error & e) { } catch (Error & e) {
e.addTrace(pos, hintfmt("while checking the NixOS module '%s'", attrPath)); e.addTrace(resolve(pos), hintfmt("while checking the NixOS module '%s'", attrPath));
reportError(e); reportError(e);
} }
}; };
std::function<void(const std::string & attrPath, Value & v, const Pos & pos)> checkHydraJobs; std::function<void(const std::string & attrPath, Value & v, const PosIdx pos)> checkHydraJobs;
checkHydraJobs = [&](const std::string & attrPath, Value & v, const Pos & pos) { checkHydraJobs = [&](const std::string & attrPath, Value & v, const PosIdx pos) {
try { try {
state->forceAttrs(v, pos); state->forceAttrs(v, pos);
@ -407,23 +413,23 @@ struct CmdFlakeCheck : FlakeCommand
throw Error("jobset should not be a derivation at top-level"); throw Error("jobset should not be a derivation at top-level");
for (auto & attr : *v.attrs) { for (auto & attr : *v.attrs) {
state->forceAttrs(*attr.value, *attr.pos); state->forceAttrs(*attr.value, attr.pos);
auto attrPath2 = attrPath + "." + (std::string) attr.name; auto attrPath2 = attrPath + "." + (std::string) attr.name;
if (state->isDerivation(*attr.value)) { if (state->isDerivation(*attr.value)) {
Activity act(*logger, lvlChatty, actUnknown, Activity act(*logger, lvlChatty, actUnknown,
fmt("checking Hydra job '%s'", attrPath2)); fmt("checking Hydra job '%s'", attrPath2));
checkDerivation(attrPath2, *attr.value, *attr.pos); checkDerivation(attrPath2, *attr.value, attr.pos);
} else } else
checkHydraJobs(attrPath2, *attr.value, *attr.pos); checkHydraJobs(attrPath2, *attr.value, attr.pos);
} }
} catch (Error & e) { } catch (Error & e) {
e.addTrace(pos, hintfmt("while checking the Hydra jobset '%s'", attrPath)); e.addTrace(resolve(pos), hintfmt("while checking the Hydra jobset '%s'", attrPath));
reportError(e); reportError(e);
} }
}; };
auto checkNixOSConfiguration = [&](const std::string & attrPath, Value & v, const Pos & pos) { auto checkNixOSConfiguration = [&](const std::string & attrPath, Value & v, const PosIdx pos) {
try { try {
Activity act(*logger, lvlChatty, actUnknown, Activity act(*logger, lvlChatty, actUnknown,
fmt("checking NixOS configuration '%s'", attrPath)); fmt("checking NixOS configuration '%s'", attrPath));
@ -433,12 +439,12 @@ struct CmdFlakeCheck : FlakeCommand
if (!state->isDerivation(*vToplevel)) if (!state->isDerivation(*vToplevel))
throw Error("attribute 'config.system.build.toplevel' is not a derivation"); throw Error("attribute 'config.system.build.toplevel' is not a derivation");
} catch (Error & e) { } catch (Error & e) {
e.addTrace(pos, hintfmt("while checking the NixOS configuration '%s'", attrPath)); e.addTrace(resolve(pos), hintfmt("while checking the NixOS configuration '%s'", attrPath));
reportError(e); reportError(e);
} }
}; };
auto checkTemplate = [&](const std::string & attrPath, Value & v, const Pos & pos) { auto checkTemplate = [&](const std::string & attrPath, Value & v, const PosIdx pos) {
try { try {
Activity act(*logger, lvlChatty, actUnknown, Activity act(*logger, lvlChatty, actUnknown,
fmt("checking template '%s'", attrPath)); fmt("checking template '%s'", attrPath));
@ -448,7 +454,7 @@ struct CmdFlakeCheck : FlakeCommand
if (auto attr = v.attrs->get(state->symbols.create("path"))) { if (auto attr = v.attrs->get(state->symbols.create("path"))) {
if (attr->name == state->symbols.create("path")) { if (attr->name == state->symbols.create("path")) {
PathSet context; PathSet context;
auto path = state->coerceToPath(*attr->pos, *attr->value, context); auto path = state->coerceToPath(attr->pos, *attr->value, context);
if (!store->isInStore(path)) if (!store->isInStore(path))
throw Error("template '%s' has a bad 'path' attribute"); throw Error("template '%s' has a bad 'path' attribute");
// TODO: recursively check the flake in 'path'. // TODO: recursively check the flake in 'path'.
@ -457,7 +463,7 @@ struct CmdFlakeCheck : FlakeCommand
throw Error("template '%s' lacks attribute 'path'", attrPath); throw Error("template '%s' lacks attribute 'path'", attrPath);
if (auto attr = v.attrs->get(state->symbols.create("description"))) if (auto attr = v.attrs->get(state->symbols.create("description")))
state->forceStringNoCtx(*attr->value, *attr->pos); state->forceStringNoCtx(*attr->value, attr->pos);
else else
throw Error("template '%s' lacks attribute 'description'", attrPath); throw Error("template '%s' lacks attribute 'description'", attrPath);
@ -467,19 +473,19 @@ struct CmdFlakeCheck : FlakeCommand
throw Error("template '%s' has unsupported attribute '%s'", attrPath, name); throw Error("template '%s' has unsupported attribute '%s'", attrPath, name);
} }
} catch (Error & e) { } catch (Error & e) {
e.addTrace(pos, hintfmt("while checking the template '%s'", attrPath)); e.addTrace(resolve(pos), hintfmt("while checking the template '%s'", attrPath));
reportError(e); reportError(e);
} }
}; };
auto checkBundler = [&](const std::string & attrPath, Value & v, const Pos & pos) { auto checkBundler = [&](const std::string & attrPath, Value & v, const PosIdx pos) {
try { try {
state->forceValue(v, pos); state->forceValue(v, pos);
if (!v.isLambda()) if (!v.isLambda())
throw Error("bundler must be a function"); throw Error("bundler must be a function");
// TODO: check types of inputs/outputs? // TODO: check types of inputs/outputs?
} catch (Error & e) { } catch (Error & e) {
e.addTrace(pos, hintfmt("while checking the template '%s'", attrPath)); e.addTrace(resolve(pos), hintfmt("while checking the template '%s'", attrPath));
reportError(e); reportError(e);
} }
}; };
@ -492,7 +498,7 @@ struct CmdFlakeCheck : FlakeCommand
enumerateOutputs(*state, enumerateOutputs(*state,
*vFlake, *vFlake,
[&](const std::string & name, Value & vOutput, const Pos & pos) { [&](const std::string & name, Value & vOutput, const PosIdx pos) {
Activity act(*logger, lvlChatty, actUnknown, Activity act(*logger, lvlChatty, actUnknown,
fmt("checking flake output '%s'", name)); fmt("checking flake output '%s'", name));
@ -516,12 +522,12 @@ struct CmdFlakeCheck : FlakeCommand
if (name == "checks") { if (name == "checks") {
state->forceAttrs(vOutput, pos); state->forceAttrs(vOutput, pos);
for (auto & attr : *vOutput.attrs) { for (auto & attr : *vOutput.attrs) {
checkSystemName(attr.name, *attr.pos); checkSystemName(attr.name, attr.pos);
state->forceAttrs(*attr.value, *attr.pos); state->forceAttrs(*attr.value, attr.pos);
for (auto & attr2 : *attr.value->attrs) { for (auto & attr2 : *attr.value->attrs) {
auto drvPath = checkDerivation( auto drvPath = checkDerivation(
fmt("%s.%s.%s", name, attr.name, attr2.name), fmt("%s.%s.%s", name, attr.name, attr2.name),
*attr2.value, *attr2.pos); *attr2.value, attr2.pos);
if (drvPath && (std::string) attr.name == settings.thisSystem.get()) if (drvPath && (std::string) attr.name == settings.thisSystem.get())
drvPaths.push_back(DerivedPath::Built{*drvPath}); drvPaths.push_back(DerivedPath::Built{*drvPath});
} }
@ -531,61 +537,61 @@ struct CmdFlakeCheck : FlakeCommand
else if (name == "formatter") { else if (name == "formatter") {
state->forceAttrs(vOutput, pos); state->forceAttrs(vOutput, pos);
for (auto & attr : *vOutput.attrs) { for (auto & attr : *vOutput.attrs) {
checkSystemName(attr.name, *attr.pos); checkSystemName(attr.name, attr.pos);
checkApp( checkApp(
fmt("%s.%s", name, attr.name), fmt("%s.%s", name, attr.name),
*attr.value, *attr.pos); *attr.value, attr.pos);
} }
} }
else if (name == "packages" || name == "devShells") { else if (name == "packages" || name == "devShells") {
state->forceAttrs(vOutput, pos); state->forceAttrs(vOutput, pos);
for (auto & attr : *vOutput.attrs) { for (auto & attr : *vOutput.attrs) {
checkSystemName(attr.name, *attr.pos); checkSystemName(attr.name, attr.pos);
state->forceAttrs(*attr.value, *attr.pos); state->forceAttrs(*attr.value, attr.pos);
for (auto & attr2 : *attr.value->attrs) for (auto & attr2 : *attr.value->attrs)
checkDerivation( checkDerivation(
fmt("%s.%s.%s", name, attr.name, attr2.name), fmt("%s.%s.%s", name, attr.name, attr2.name),
*attr2.value, *attr2.pos); *attr2.value, attr2.pos);
} }
} }
else if (name == "apps") { else if (name == "apps") {
state->forceAttrs(vOutput, pos); state->forceAttrs(vOutput, pos);
for (auto & attr : *vOutput.attrs) { for (auto & attr : *vOutput.attrs) {
checkSystemName(attr.name, *attr.pos); checkSystemName(attr.name, attr.pos);
state->forceAttrs(*attr.value, *attr.pos); state->forceAttrs(*attr.value, attr.pos);
for (auto & attr2 : *attr.value->attrs) for (auto & attr2 : *attr.value->attrs)
checkApp( checkApp(
fmt("%s.%s.%s", name, attr.name, attr2.name), fmt("%s.%s.%s", name, attr.name, attr2.name),
*attr2.value, *attr2.pos); *attr2.value, attr2.pos);
} }
} }
else if (name == "defaultPackage" || name == "devShell") { else if (name == "defaultPackage" || name == "devShell") {
state->forceAttrs(vOutput, pos); state->forceAttrs(vOutput, pos);
for (auto & attr : *vOutput.attrs) { for (auto & attr : *vOutput.attrs) {
checkSystemName(attr.name, *attr.pos); checkSystemName(attr.name, attr.pos);
checkDerivation( checkDerivation(
fmt("%s.%s", name, attr.name), fmt("%s.%s", name, attr.name),
*attr.value, *attr.pos); *attr.value, attr.pos);
} }
} }
else if (name == "defaultApp") { else if (name == "defaultApp") {
state->forceAttrs(vOutput, pos); state->forceAttrs(vOutput, pos);
for (auto & attr : *vOutput.attrs) { for (auto & attr : *vOutput.attrs) {
checkSystemName(attr.name, *attr.pos); checkSystemName(attr.name, attr.pos);
checkApp( checkApp(
fmt("%s.%s", name, attr.name), fmt("%s.%s", name, attr.name),
*attr.value, *attr.pos); *attr.value, attr.pos);
} }
} }
else if (name == "legacyPackages") { else if (name == "legacyPackages") {
state->forceAttrs(vOutput, pos); state->forceAttrs(vOutput, pos);
for (auto & attr : *vOutput.attrs) { for (auto & attr : *vOutput.attrs) {
checkSystemName(attr.name, *attr.pos); checkSystemName(attr.name, attr.pos);
// FIXME: do getDerivations? // FIXME: do getDerivations?
} }
} }
@ -597,7 +603,7 @@ struct CmdFlakeCheck : FlakeCommand
state->forceAttrs(vOutput, pos); state->forceAttrs(vOutput, pos);
for (auto & attr : *vOutput.attrs) for (auto & attr : *vOutput.attrs)
checkOverlay(fmt("%s.%s", name, attr.name), checkOverlay(fmt("%s.%s", name, attr.name),
*attr.value, *attr.pos); *attr.value, attr.pos);
} }
else if (name == "nixosModule") else if (name == "nixosModule")
@ -607,14 +613,14 @@ struct CmdFlakeCheck : FlakeCommand
state->forceAttrs(vOutput, pos); state->forceAttrs(vOutput, pos);
for (auto & attr : *vOutput.attrs) for (auto & attr : *vOutput.attrs)
checkModule(fmt("%s.%s", name, attr.name), checkModule(fmt("%s.%s", name, attr.name),
*attr.value, *attr.pos); *attr.value, attr.pos);
} }
else if (name == "nixosConfigurations") { else if (name == "nixosConfigurations") {
state->forceAttrs(vOutput, pos); state->forceAttrs(vOutput, pos);
for (auto & attr : *vOutput.attrs) for (auto & attr : *vOutput.attrs)
checkNixOSConfiguration(fmt("%s.%s", name, attr.name), checkNixOSConfiguration(fmt("%s.%s", name, attr.name),
*attr.value, *attr.pos); *attr.value, attr.pos);
} }
else if (name == "hydraJobs") else if (name == "hydraJobs")
@ -627,28 +633,28 @@ struct CmdFlakeCheck : FlakeCommand
state->forceAttrs(vOutput, pos); state->forceAttrs(vOutput, pos);
for (auto & attr : *vOutput.attrs) for (auto & attr : *vOutput.attrs)
checkTemplate(fmt("%s.%s", name, attr.name), checkTemplate(fmt("%s.%s", name, attr.name),
*attr.value, *attr.pos); *attr.value, attr.pos);
} }
else if (name == "defaultBundler") { else if (name == "defaultBundler") {
state->forceAttrs(vOutput, pos); state->forceAttrs(vOutput, pos);
for (auto & attr : *vOutput.attrs) { for (auto & attr : *vOutput.attrs) {
checkSystemName(attr.name, *attr.pos); checkSystemName(attr.name, attr.pos);
checkBundler( checkBundler(
fmt("%s.%s", name, attr.name), fmt("%s.%s", name, attr.name),
*attr.value, *attr.pos); *attr.value, attr.pos);
} }
} }
else if (name == "bundlers") { else if (name == "bundlers") {
state->forceAttrs(vOutput, pos); state->forceAttrs(vOutput, pos);
for (auto & attr : *vOutput.attrs) { for (auto & attr : *vOutput.attrs) {
checkSystemName(attr.name, *attr.pos); checkSystemName(attr.name, attr.pos);
state->forceAttrs(*attr.value, *attr.pos); state->forceAttrs(*attr.value, attr.pos);
for (auto & attr2 : *attr.value->attrs) { for (auto & attr2 : *attr.value->attrs) {
checkBundler( checkBundler(
fmt("%s.%s.%s", name, attr.name, attr2.name), fmt("%s.%s.%s", name, attr.name, attr2.name),
*attr2.value, *attr2.pos); *attr2.value, attr2.pos);
} }
} }
} }
@ -657,7 +663,7 @@ struct CmdFlakeCheck : FlakeCommand
warn("unknown flake output '%s'", name); warn("unknown flake output '%s'", name);
} catch (Error & e) { } catch (Error & e) {
e.addTrace(pos, hintfmt("while checking flake output '%s'", name)); e.addTrace(resolve(pos), hintfmt("while checking flake output '%s'", name));
reportError(e); reportError(e);
} }
}); });

View file

@ -467,7 +467,8 @@ bool NixRepl::processLine(std::string line)
auto filename = state->coerceToString(noPos, v, context); auto filename = state->coerceToString(noPos, v, context);
return {state->symbols.create(*filename), 0}; return {state->symbols.create(*filename), 0};
} else if (v.isLambda()) { } else if (v.isLambda()) {
return {v.lambda.fun->pos.file, v.lambda.fun->pos.line}; auto pos = state->positions[v.lambda.fun->pos];
return {pos.file, pos.line};
} else { } else {
// assume it's a derivation // assume it's a derivation
return findPackageFilename(*state, v, arg); return findPackageFilename(*state, v, arg);
@ -498,7 +499,7 @@ bool NixRepl::processLine(std::string line)
Value v, f, result; Value v, f, result;
evalString(arg, v); evalString(arg, v);
evalString("drv: (import <nixpkgs> {}).runCommand \"shell\" { buildInputs = [ drv ]; } \"\"", f); evalString("drv: (import <nixpkgs> {}).runCommand \"shell\" { buildInputs = [ drv ]; } \"\"", f);
state->callFunction(f, v, result, Pos()); state->callFunction(f, v, result, PosIdx());
StorePath drvPath = getDerivationPath(result); StorePath drvPath = getDerivationPath(result);
runNix("nix-shell", {state->store->printStorePath(drvPath)}); runNix("nix-shell", {state->store->printStorePath(drvPath)});
@ -799,7 +800,7 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
Bindings::iterator i = v.attrs->find(state->sDrvPath); Bindings::iterator i = v.attrs->find(state->sDrvPath);
PathSet context; PathSet context;
if (i != v.attrs->end()) if (i != v.attrs->end())
str << state->store->printStorePath(state->coerceToStorePath(*i->pos, *i->value, context)); str << state->store->printStorePath(state->coerceToStorePath(i->pos, *i->value, context));
else else
str << "???"; str << "???";
str << "»"; str << "»";
@ -861,7 +862,7 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
case nFunction: case nFunction:
if (v.isLambda()) { if (v.isLambda()) {
std::ostringstream s; std::ostringstream s;
s << v.lambda.fun->pos; s << state->positions[v.lambda.fun->pos];
str << ANSI_BLUE "«lambda @ " << filterANSIEscapes(s.str()) << "»" ANSI_NORMAL; str << ANSI_BLUE "«lambda @ " << filterANSIEscapes(s.str()) << "»" ANSI_NORMAL;
} else if (v.isPrimOp()) { } else if (v.isPrimOp()) {
str << ANSI_MAGENTA "«primop»" ANSI_NORMAL; str << ANSI_MAGENTA "«primop»" ANSI_NORMAL;

View file

@ -13,7 +13,7 @@ MySettings mySettings;
static GlobalConfig::Register rs(&mySettings); static GlobalConfig::Register rs(&mySettings);
static void prim_anotherNull (EvalState & state, const Pos & pos, Value ** args, Value & v) static void prim_anotherNull (EvalState & state, const PosIdx pos, Value ** args, Value & v)
{ {
if (mySettings.settingSet) if (mySettings.settingSet)
v.mkNull(); v.mkNull();