Merge pull request #10322 from edolstra/finish-value

Ensure immutability of non-thunk values
This commit is contained in:
Eelco Dolstra 2024-04-17 16:42:17 +02:00 committed by GitHub
commit 4638ddd855
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
36 changed files with 534 additions and 568 deletions

View file

@ -49,7 +49,7 @@ Value * InstallableFlake::getFlakeOutputs(EvalState & state, const flake::Locked
callFlake(state, lockedFlake, *vFlake); callFlake(state, lockedFlake, *vFlake);
auto aOutputs = vFlake->attrs->get(state.symbols.create("outputs")); auto aOutputs = vFlake->attrs()->get(state.symbols.create("outputs"));
assert(aOutputs); assert(aOutputs);
state.forceValue(*aOutputs->value, aOutputs->value->determinePos(noPos)); state.forceValue(*aOutputs->value, aOutputs->value->determinePos(noPos));

View file

@ -289,7 +289,7 @@ void SourceExprCommand::completeInstallable(AddCompletions & completions, std::s
state->autoCallFunction(*autoArgs, v1, v2); state->autoCallFunction(*autoArgs, v1, v2);
if (v2.type() == nAttrs) { if (v2.type() == nAttrs) {
for (auto & i : *v2.attrs) { for (auto & i : *v2.attrs()) {
std::string name = state->symbols[i.name]; std::string name = state->symbols[i.name];
if (name.find(searchWord) == 0) { if (name.find(searchWord) == 0) {
if (prefix_ == "") if (prefix_ == "")
@ -461,7 +461,7 @@ ref<eval_cache::EvalCache> openEvalCache(
state.forceAttrs(*vFlake, noPos, "while parsing cached flake data"); state.forceAttrs(*vFlake, noPos, "while parsing cached flake data");
auto aOutputs = vFlake->attrs->get(state.symbols.create("outputs")); auto aOutputs = vFlake->attrs()->get(state.symbols.create("outputs"));
assert(aOutputs); assert(aOutputs);
return aOutputs->value; return aOutputs->value;

View file

@ -290,7 +290,7 @@ StringSet NixRepl::completePrefix(const std::string & prefix)
e->eval(*state, *env, v); e->eval(*state, *env, v);
state->forceAttrs(v, noPos, "while evaluating an attrset for the purpose of completion (this error should not be displayed; file an issue?)"); state->forceAttrs(v, noPos, "while evaluating an attrset for the purpose of completion (this error should not be displayed; file an issue?)");
for (auto & i : *v.attrs) { for (auto & i : *v.attrs()) {
std::string_view name = state->symbols[i.name]; std::string_view name = state->symbols[i.name];
if (name.substr(0, cur2.size()) != cur2) continue; if (name.substr(0, cur2.size()) != cur2) continue;
completions.insert(concatStrings(prev, expr, ".", name)); completions.insert(concatStrings(prev, expr, ".", name));
@ -490,7 +490,7 @@ ProcessLineResult NixRepl::processLine(std::string line)
auto path = state->coerceToPath(noPos, v, context, "while evaluating the filename to edit"); auto path = state->coerceToPath(noPos, v, context, "while evaluating the filename to edit");
return {path, 0}; return {path, 0};
} else if (v.isLambda()) { } else if (v.isLambda()) {
auto pos = state->positions[v.lambda.fun->pos]; auto pos = state->positions[v.payload.lambda.fun->pos];
if (auto path = std::get_if<SourcePath>(&pos.origin)) if (auto path = std::get_if<SourcePath>(&pos.origin))
return {*path, pos.line}; return {*path, pos.line};
else else
@ -742,17 +742,17 @@ void NixRepl::loadFiles()
void NixRepl::addAttrsToScope(Value & attrs) void NixRepl::addAttrsToScope(Value & attrs)
{ {
state->forceAttrs(attrs, [&]() { return attrs.determinePos(noPos); }, "while evaluating an attribute set to be merged in the global scope"); state->forceAttrs(attrs, [&]() { return attrs.determinePos(noPos); }, "while evaluating an attribute set to be merged in the global scope");
if (displ + attrs.attrs->size() >= envSize) if (displ + attrs.attrs()->size() >= envSize)
throw Error("environment full; cannot add more variables"); throw Error("environment full; cannot add more variables");
for (auto & i : *attrs.attrs) { for (auto & i : *attrs.attrs()) {
staticEnv->vars.emplace_back(i.name, displ); staticEnv->vars.emplace_back(i.name, displ);
env->values[displ++] = i.value; env->values[displ++] = i.value;
varNames.emplace(state->symbols[i.name]); varNames.emplace(state->symbols[i.name]);
} }
staticEnv->sort(); staticEnv->sort();
staticEnv->deduplicate(); staticEnv->deduplicate();
notice("Added %1% variables.", attrs.attrs->size()); notice("Added %1% variables.", attrs.attrs()->size());
} }

View file

@ -161,7 +161,7 @@ bool nix_get_bool(nix_c_context * context, const Value * value)
try { try {
auto & v = check_value_not_null(value); auto & v = check_value_not_null(value);
assert(v.type() == nix::nBool); assert(v.type() == nix::nBool);
return v.boolean; return v.boolean();
} }
NIXC_CATCH_ERRS_RES(false); NIXC_CATCH_ERRS_RES(false);
} }
@ -192,7 +192,7 @@ const char * nix_get_path_string(nix_c_context * context, const Value * value)
// We could use v.path().to_string().c_str(), but I'm concerned this // We could use v.path().to_string().c_str(), but I'm concerned this
// crashes. Looks like .path() allocates a CanonPath with a copy of the // crashes. Looks like .path() allocates a CanonPath with a copy of the
// string, then it gets the underlying data from that. // string, then it gets the underlying data from that.
return v._path.path; return v.payload.path.path;
} }
NIXC_CATCH_ERRS_NULL NIXC_CATCH_ERRS_NULL
} }
@ -216,7 +216,7 @@ unsigned int nix_get_attrs_size(nix_c_context * context, const Value * value)
try { try {
auto & v = check_value_not_null(value); auto & v = check_value_not_null(value);
assert(v.type() == nix::nAttrs); assert(v.type() == nix::nAttrs);
return v.attrs->size(); return v.attrs()->size();
} }
NIXC_CATCH_ERRS_RES(0); NIXC_CATCH_ERRS_RES(0);
} }
@ -228,7 +228,7 @@ double nix_get_float(nix_c_context * context, const Value * value)
try { try {
auto & v = check_value_not_null(value); auto & v = check_value_not_null(value);
assert(v.type() == nix::nFloat); assert(v.type() == nix::nFloat);
return v.fpoint; return v.fpoint();
} }
NIXC_CATCH_ERRS_RES(0.0); NIXC_CATCH_ERRS_RES(0.0);
} }
@ -240,7 +240,7 @@ int64_t nix_get_int(nix_c_context * context, const Value * value)
try { try {
auto & v = check_value_not_null(value); auto & v = check_value_not_null(value);
assert(v.type() == nix::nInt); assert(v.type() == nix::nInt);
return v.integer; return v.integer();
} }
NIXC_CATCH_ERRS_RES(0); NIXC_CATCH_ERRS_RES(0);
} }
@ -252,7 +252,7 @@ ExternalValue * nix_get_external(nix_c_context * context, Value * value)
try { try {
auto & v = check_value_not_null(value); auto & v = check_value_not_null(value);
assert(v.type() == nix::nExternal); assert(v.type() == nix::nExternal);
return (ExternalValue *) v.external; return (ExternalValue *) v.external();
} }
NIXC_CATCH_ERRS_NULL; NIXC_CATCH_ERRS_NULL;
} }
@ -281,7 +281,7 @@ Value * nix_get_attr_byname(nix_c_context * context, const Value * value, EvalSt
auto & v = check_value_not_null(value); auto & v = check_value_not_null(value);
assert(v.type() == nix::nAttrs); assert(v.type() == nix::nAttrs);
nix::Symbol s = state->state.symbols.create(name); nix::Symbol s = state->state.symbols.create(name);
auto attr = v.attrs->get(s); auto attr = v.attrs()->get(s);
if (attr) { if (attr) {
nix_gc_incref(nullptr, attr->value); nix_gc_incref(nullptr, attr->value);
state->state.forceValue(*attr->value, nix::noPos); state->state.forceValue(*attr->value, nix::noPos);
@ -301,7 +301,7 @@ bool nix_has_attr_byname(nix_c_context * context, const Value * value, EvalState
auto & v = check_value_not_null(value); auto & v = check_value_not_null(value);
assert(v.type() == nix::nAttrs); assert(v.type() == nix::nAttrs);
nix::Symbol s = state->state.symbols.create(name); nix::Symbol s = state->state.symbols.create(name);
auto attr = v.attrs->get(s); auto attr = v.attrs()->get(s);
if (attr) if (attr)
return true; return true;
return false; return false;
@ -316,7 +316,7 @@ nix_get_attr_byidx(nix_c_context * context, const Value * value, EvalState * sta
context->last_err_code = NIX_OK; context->last_err_code = NIX_OK;
try { try {
auto & v = check_value_not_null(value); auto & v = check_value_not_null(value);
const nix::Attr & a = (*v.attrs)[i]; const nix::Attr & a = (*v.attrs())[i];
*name = ((const std::string &) (state->state.symbols[a.name])).c_str(); *name = ((const std::string &) (state->state.symbols[a.name])).c_str();
nix_gc_incref(nullptr, a.value); nix_gc_incref(nullptr, a.value);
state->state.forceValue(*a.value, nix::noPos); state->state.forceValue(*a.value, nix::noPos);
@ -331,7 +331,7 @@ const char * nix_get_attr_name_byidx(nix_c_context * context, const Value * valu
context->last_err_code = NIX_OK; context->last_err_code = NIX_OK;
try { try {
auto & v = check_value_not_null(value); auto & v = check_value_not_null(value);
const nix::Attr & a = (*v.attrs)[i]; const nix::Attr & a = (*v.attrs())[i];
return ((const std::string &) (state->state.symbols[a.name])).c_str(); return ((const std::string &) (state->state.symbols[a.name])).c_str();
} }
NIXC_CATCH_ERRS_NULL NIXC_CATCH_ERRS_NULL

View file

@ -72,10 +72,10 @@ std::pair<Value *, PosIdx> findAlongAttrPath(EvalState & state, const std::strin
if (attr.empty()) if (attr.empty())
throw Error("empty attribute name in selection path '%1%'", attrPath); throw Error("empty attribute name in selection path '%1%'", attrPath);
Bindings::iterator a = v->attrs->find(state.symbols.create(attr)); auto a = v->attrs()->get(state.symbols.create(attr));
if (a == v->attrs->end()) { if (!a) {
std::set<std::string> attrNames; std::set<std::string> attrNames;
for (auto & attr : *v->attrs) for (auto & attr : *v->attrs())
attrNames.insert(state.symbols[attr.name]); attrNames.insert(state.symbols[attr.name]);
auto suggestions = Suggestions::bestMatches(attrNames, attr); auto suggestions = Suggestions::bestMatches(attrNames, attr);

View file

@ -23,23 +23,6 @@ Bindings * EvalState::allocBindings(size_t capacity)
} }
/* Create a new attribute named 'name' on an existing attribute set stored
in 'vAttrs' and return the newly allocated Value which is associated with
this attribute. */
Value * EvalState::allocAttr(Value & vAttrs, Symbol name)
{
Value * v = allocValue();
vAttrs.attrs->push_back(Attr(name, v));
return v;
}
Value * EvalState::allocAttr(Value & vAttrs, std::string_view name)
{
return allocAttr(vAttrs, symbols.create(name));
}
Value & BindingsBuilder::alloc(Symbol name, PosIdx pos) Value & BindingsBuilder::alloc(Symbol name, PosIdx pos)
{ {
auto value = state.allocValue(); auto value = state.allocValue();

View file

@ -65,24 +65,26 @@ public:
typedef Attr * iterator; typedef Attr * iterator;
typedef const Attr * const_iterator;
void push_back(const Attr & attr) void push_back(const Attr & attr)
{ {
assert(size_ < capacity_); assert(size_ < capacity_);
attrs[size_++] = attr; attrs[size_++] = attr;
} }
iterator find(Symbol name) const_iterator find(Symbol name) const
{ {
Attr key(name, 0); Attr key(name, 0);
iterator i = std::lower_bound(begin(), end(), key); const_iterator i = std::lower_bound(begin(), end(), key);
if (i != end() && i->name == name) return i; if (i != end() && i->name == name) return i;
return end(); return end();
} }
Attr * get(Symbol name) const Attr * get(Symbol name) const
{ {
Attr key(name, 0); Attr key(name, 0);
iterator i = std::lower_bound(begin(), end(), key); const_iterator i = std::lower_bound(begin(), end(), key);
if (i != end() && i->name == name) return &*i; if (i != end() && i->name == name) return &*i;
return nullptr; return nullptr;
} }
@ -90,14 +92,22 @@ public:
iterator begin() { return &attrs[0]; } iterator begin() { return &attrs[0]; }
iterator end() { return &attrs[size_]; } iterator end() { return &attrs[size_]; }
const_iterator begin() const { return &attrs[0]; }
const_iterator end() const { return &attrs[size_]; }
Attr & operator[](size_t pos) Attr & operator[](size_t pos)
{ {
return attrs[pos]; return attrs[pos];
} }
const Attr & operator[](size_t pos) const
{
return attrs[pos];
}
void sort(); void sort();
size_t capacity() { return capacity_; } size_t capacity() const { return capacity_; }
/** /**
* Returns the attributes in lexicographically sorted order. * Returns the attributes in lexicographically sorted order.
@ -166,6 +176,20 @@ public:
{ {
return bindings; return bindings;
} }
size_t capacity()
{
return bindings->capacity();
}
void grow(Bindings * newBindings)
{
for (auto & i : *bindings)
newBindings->push_back(i);
bindings = newBindings;
}
friend class ExprAttrs;
}; };
} }

View file

@ -387,7 +387,7 @@ Value & AttrCursor::getValue()
if (parent) { if (parent) {
auto & vParent = parent->first->getValue(); auto & vParent = parent->first->getValue();
root->state.forceAttrs(vParent, noPos, "while searching for an attribute"); root->state.forceAttrs(vParent, noPos, "while searching for an attribute");
auto attr = vParent.attrs->get(parent->second); auto attr = vParent.attrs()->get(parent->second);
if (!attr) if (!attr)
throw Error("attribute '%s' is unexpectedly missing", getAttrPathStr()); throw Error("attribute '%s' is unexpectedly missing", getAttrPathStr());
_value = allocRootValue(attr->value); _value = allocRootValue(attr->value);
@ -448,9 +448,9 @@ Value & AttrCursor::forceValue()
cachedValue = {root->db->setString(getKey(), path.abs()), string_t{path.abs(), {}}}; cachedValue = {root->db->setString(getKey(), path.abs()), string_t{path.abs(), {}}};
} }
else if (v.type() == nBool) else if (v.type() == nBool)
cachedValue = {root->db->setBool(getKey(), v.boolean), v.boolean}; cachedValue = {root->db->setBool(getKey(), v.boolean()), v.boolean()};
else if (v.type() == nInt) else if (v.type() == nInt)
cachedValue = {root->db->setInt(getKey(), v.integer), int_t{v.integer}}; cachedValue = {root->db->setInt(getKey(), v.integer()), int_t{v.integer()}};
else if (v.type() == nAttrs) else if (v.type() == nAttrs)
; // FIXME: do something? ; // FIXME: do something?
else else
@ -510,7 +510,7 @@ std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(Symbol name, bool forceErro
return nullptr; return nullptr;
//error<TypeError>("'%s' is not an attribute set", getAttrPathStr()).debugThrow(); //error<TypeError>("'%s' is not an attribute set", getAttrPathStr()).debugThrow();
auto attr = v.attrs->get(name); auto attr = v.attrs()->get(name);
if (!attr) { if (!attr) {
if (root->db) { if (root->db) {
@ -652,7 +652,7 @@ bool AttrCursor::getBool()
if (v.type() != nBool) if (v.type() != nBool)
root->state.error<TypeError>("'%s' is not a Boolean", getAttrPathStr()).debugThrow(); root->state.error<TypeError>("'%s' is not a Boolean", getAttrPathStr()).debugThrow();
return v.boolean; return v.boolean();
} }
NixInt AttrCursor::getInt() NixInt AttrCursor::getInt()
@ -674,7 +674,7 @@ NixInt AttrCursor::getInt()
if (v.type() != nInt) if (v.type() != nInt)
root->state.error<TypeError>("'%s' is not an integer", getAttrPathStr()).debugThrow(); root->state.error<TypeError>("'%s' is not an integer", getAttrPathStr()).debugThrow();
return v.integer; return v.integer();
} }
std::vector<std::string> AttrCursor::getListOfStrings() std::vector<std::string> AttrCursor::getListOfStrings()
@ -730,7 +730,7 @@ std::vector<Symbol> AttrCursor::getAttrs()
root->state.error<TypeError>("'%s' is not an attribute set", getAttrPathStr()).debugThrow(); root->state.error<TypeError>("'%s' is not an attribute set", getAttrPathStr()).debugThrow();
std::vector<Symbol> attrs; std::vector<Symbol> attrs;
for (auto & attr : *getValue().attrs) for (auto & attr : *getValue().attrs())
attrs.push_back(attr.name); attrs.push_back(attr.name);
std::sort(attrs.begin(), attrs.end(), [&](Symbol a, Symbol b) { std::sort(attrs.begin(), attrs.end(), [&](Symbol a, Symbol b) {
std::string_view sa = root->state.symbols[a], sb = root->state.symbols[b]; std::string_view sa = root->state.symbols[a], sb = root->state.symbols[b];

View file

@ -85,8 +85,8 @@ Env & EvalState::allocEnv(size_t size)
void EvalState::forceValue(Value & v, const PosIdx pos) void EvalState::forceValue(Value & v, const PosIdx pos)
{ {
if (v.isThunk()) { if (v.isThunk()) {
Env * env = v.thunk.env; Env * env = v.payload.thunk.env;
Expr * expr = v.thunk.expr; Expr * expr = v.payload.thunk.expr;
try { try {
v.mkBlackhole(); v.mkBlackhole();
//checkInterrupt(); //checkInterrupt();
@ -98,7 +98,7 @@ void EvalState::forceValue(Value & v, const PosIdx pos)
} }
} }
else if (v.isApp()) else if (v.isApp())
callFunction(*v.app.left, *v.app.right, v, pos); callFunction(*v.payload.app.left, *v.payload.app.right, v, pos);
} }

View file

@ -131,7 +131,7 @@ void Value::print(EvalState & state, std::ostream & str, PrintOptions options)
const Value * getPrimOp(const Value &v) { const Value * getPrimOp(const Value &v) {
const Value * primOp = &v; const Value * primOp = &v;
while (primOp->isPrimOpApp()) { while (primOp->isPrimOpApp()) {
primOp = primOp->primOpApp.left; primOp = primOp->payload.primOpApp.left;
} }
assert(primOp->isPrimOp()); assert(primOp->isPrimOp());
return primOp; return primOp;
@ -163,12 +163,12 @@ std::string showType(const Value & v)
#pragma GCC diagnostic push #pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wswitch-enum" #pragma GCC diagnostic ignored "-Wswitch-enum"
switch (v.internalType) { switch (v.internalType) {
case tString: return v.string.context ? "a string with context" : "a string"; case tString: return v.payload.string.context ? "a string with context" : "a string";
case tPrimOp: case tPrimOp:
return fmt("the built-in function '%s'", std::string(v.primOp->name)); return fmt("the built-in function '%s'", std::string(v.payload.primOp->name));
case tPrimOpApp: case tPrimOpApp:
return fmt("the partially applied built-in function '%s'", std::string(getPrimOp(v)->primOp->name)); return fmt("the partially applied built-in function '%s'", std::string(getPrimOp(v)->payload.primOp->name));
case tExternal: return v.external->showType(); case tExternal: return v.external()->showType();
case tThunk: return v.isBlackhole() ? "a black hole" : "a thunk"; case tThunk: return v.isBlackhole() ? "a black hole" : "a thunk";
case tApp: return "a function application"; case tApp: return "a function application";
default: default:
@ -183,9 +183,9 @@ PosIdx Value::determinePos(const PosIdx pos) const
#pragma GCC diagnostic push #pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wswitch-enum" #pragma GCC diagnostic ignored "-Wswitch-enum"
switch (internalType) { switch (internalType) {
case tAttrs: return attrs->pos; case tAttrs: return attrs()->pos;
case tLambda: return lambda.fun->pos; case tLambda: return payload.lambda.fun->pos;
case tApp: return app.left->determinePos(pos); case tApp: return payload.app.left->determinePos(pos);
default: return pos; default: return pos;
} }
#pragma GCC diagnostic pop #pragma GCC diagnostic pop
@ -197,10 +197,10 @@ bool Value::isTrivial() const
internalType != tApp internalType != tApp
&& internalType != tPrimOpApp && internalType != tPrimOpApp
&& (internalType != tThunk && (internalType != tThunk
|| (dynamic_cast<ExprAttrs *>(thunk.expr) || (dynamic_cast<ExprAttrs *>(payload.thunk.expr)
&& ((ExprAttrs *) thunk.expr)->dynamicAttrs.empty()) && ((ExprAttrs *) payload.thunk.expr)->dynamicAttrs.empty())
|| dynamic_cast<ExprLambda *>(thunk.expr) || dynamic_cast<ExprLambda *>(payload.thunk.expr)
|| dynamic_cast<ExprList *>(thunk.expr)); || dynamic_cast<ExprList *>(payload.thunk.expr));
} }
@ -584,7 +584,7 @@ void EvalState::addConstant(const std::string & name, Value * v, Constant info)
/* Install value the base environment. */ /* Install value the base environment. */
staticBaseEnv->vars.emplace_back(symbols.create(name), baseEnvDispl); staticBaseEnv->vars.emplace_back(symbols.create(name), baseEnvDispl);
baseEnv.values[baseEnvDispl++] = v; baseEnv.values[baseEnvDispl++] = v;
baseEnv.values[0]->attrs->push_back(Attr(symbols.create(name2), v)); baseEnv.values[0]->payload.attrs->push_back(Attr(symbols.create(name2), v));
} }
} }
@ -597,32 +597,30 @@ void PrimOp::check()
} }
std::ostream & operator<<(std::ostream & output, PrimOp & primOp) std::ostream & operator<<(std::ostream & output, const PrimOp & primOp)
{ {
output << "primop " << primOp.name; output << "primop " << primOp.name;
return output; return output;
} }
PrimOp * Value::primOpAppPrimOp() const const PrimOp * Value::primOpAppPrimOp() const
{ {
Value * left = primOpApp.left; Value * left = payload.primOpApp.left;
while (left && !left->isPrimOp()) { while (left && !left->isPrimOp()) {
left = left->primOpApp.left; left = left->payload.primOpApp.left;
} }
if (!left) if (!left)
return nullptr; return nullptr;
return left->primOp; return left->primOp();
} }
void Value::mkPrimOp(PrimOp * p) void Value::mkPrimOp(PrimOp * p)
{ {
p->check(); p->check();
clearValue(); finishValue(tPrimOp, { .primOp = p });
internalType = tPrimOp;
primOp = p;
} }
@ -650,14 +648,14 @@ Value * EvalState::addPrimOp(PrimOp && primOp)
v->mkPrimOp(new PrimOp(primOp)); v->mkPrimOp(new PrimOp(primOp));
staticBaseEnv->vars.emplace_back(envName, baseEnvDispl); staticBaseEnv->vars.emplace_back(envName, baseEnvDispl);
baseEnv.values[baseEnvDispl++] = v; baseEnv.values[baseEnvDispl++] = v;
baseEnv.values[0]->attrs->push_back(Attr(symbols.create(primOp.name), v)); baseEnv.values[0]->payload.attrs->push_back(Attr(symbols.create(primOp.name), v));
return v; return v;
} }
Value & EvalState::getBuiltin(const std::string & name) Value & EvalState::getBuiltin(const std::string & name)
{ {
return *baseEnv.values[0]->attrs->find(symbols.create(name))->value; return *baseEnv.values[0]->attrs()->find(symbols.create(name))->value;
} }
@ -665,12 +663,12 @@ std::optional<EvalState::Doc> EvalState::getDoc(Value & v)
{ {
if (v.isPrimOp()) { if (v.isPrimOp()) {
auto v2 = &v; auto v2 = &v;
if (auto * doc = v2->primOp->doc) if (auto * doc = v2->primOp()->doc)
return Doc { return Doc {
.pos = {}, .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,
.doc = doc, .doc = doc,
}; };
} }
@ -694,8 +692,8 @@ void printWithBindings(const SymbolTable & st, const Env & env)
if (!env.values[0]->isThunk()) { if (!env.values[0]->isThunk()) {
std::cout << "with: "; std::cout << "with: ";
std::cout << ANSI_MAGENTA; std::cout << ANSI_MAGENTA;
Bindings::iterator j = env.values[0]->attrs->begin(); auto j = env.values[0]->attrs()->begin();
while (j != env.values[0]->attrs->end()) { while (j != env.values[0]->attrs()->end()) {
std::cout << st[j->name] << " "; std::cout << st[j->name] << " ";
++j; ++j;
} }
@ -749,11 +747,8 @@ void mapStaticEnvBindings(const SymbolTable & st, const StaticEnv & se, const En
if (se.isWith && !env.values[0]->isThunk()) { if (se.isWith && !env.values[0]->isThunk()) {
// add 'with' bindings. // add 'with' bindings.
Bindings::iterator j = env.values[0]->attrs->begin(); for (auto & j : *env.values[0]->attrs())
while (j != env.values[0]->attrs->end()) { vm[st[j.name]] = j.value;
vm[st[j->name]] = j->value;
++j;
}
} else { } else {
// iterate through staticenv bindings and add them. // iterate through staticenv bindings and add them.
for (auto & i : se.vars) for (auto & i : se.vars)
@ -873,28 +868,28 @@ void Value::mkString(std::string_view s)
} }
static void copyContextToValue(Value & v, const NixStringContext & context) static const char * * encodeContext(const NixStringContext & context)
{ {
if (!context.empty()) { if (!context.empty()) {
size_t n = 0; size_t n = 0;
v.string.context = (const char * *) auto ctx = (const char * *)
allocBytes((context.size() + 1) * sizeof(char *)); allocBytes((context.size() + 1) * sizeof(char *));
for (auto & i : context) for (auto & i : context)
v.string.context[n++] = dupString(i.to_string().c_str()); ctx[n++] = dupString(i.to_string().c_str());
v.string.context[n] = 0; ctx[n] = 0;
} return ctx;
} else
return nullptr;
} }
void Value::mkString(std::string_view s, const NixStringContext & context) void Value::mkString(std::string_view s, const NixStringContext & context)
{ {
mkString(s); mkString(makeImmutableString(s), encodeContext(context));
copyContextToValue(*this, context);
} }
void Value::mkStringMove(const char * s, const NixStringContext & context) void Value::mkStringMove(const char * s, const NixStringContext & context)
{ {
mkString(s); mkString(s, encodeContext(context));
copyContextToValue(*this, context);
} }
void Value::mkPath(const SourcePath & path) void Value::mkPath(const SourcePath & path)
@ -917,8 +912,7 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval)
auto * fromWith = var.fromWith; auto * fromWith = var.fromWith;
while (1) { while (1) {
forceAttrs(*env->values[0], fromWith->pos, "while evaluating the first subexpression of a with expression"); forceAttrs(*env->values[0], fromWith->pos, "while evaluating the first subexpression of a with expression");
Bindings::iterator j = env->values[0]->attrs->find(var.name); if (auto j = env->values[0]->attrs()->get(var.name)) {
if (j != env->values[0]->attrs->end()) {
if (countCalls) attrSelects[j->pos]++; if (countCalls) attrSelects[j->pos]++;
return j->value; return j->value;
} }
@ -1166,7 +1160,7 @@ inline bool EvalState::evalBool(Env & env, Expr * e, const PosIdx pos, std::stri
showType(v), showType(v),
ValuePrinter(*this, v, errorPrintOptions) ValuePrinter(*this, v, errorPrintOptions)
).atPos(pos).withFrame(env, *e).debugThrow(); ).atPos(pos).withFrame(env, *e).debugThrow();
return v.boolean; return v.boolean();
} catch (Error & e) { } catch (Error & e) {
e.addTrace(positions[pos], errorCtx); e.addTrace(positions[pos], errorCtx);
throw; throw;
@ -1234,8 +1228,9 @@ Env * ExprAttrs::buildInheritFromEnv(EvalState & state, Env & up)
void ExprAttrs::eval(EvalState & state, Env & env, Value & v) void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
{ {
v.mkAttrs(state.buildBindings(attrs.size() + dynamicAttrs.size()).finish()); auto bindings = state.buildBindings(attrs.size() + dynamicAttrs.size());
auto dynamicEnv = &env; auto dynamicEnv = &env;
bool sort = false;
if (recursive) { if (recursive) {
/* Create a new environment that contains the attributes in /* Create a new environment that contains the attributes in
@ -1260,7 +1255,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
} else } else
vAttr = i.second.e->maybeThunk(state, *i.second.chooseByKind(&env2, &env, inheritEnv)); vAttr = i.second.e->maybeThunk(state, *i.second.chooseByKind(&env2, &env, inheritEnv));
env2.values[displ++] = vAttr; env2.values[displ++] = vAttr;
v.attrs->push_back(Attr(i.first, vAttr, i.second.pos)); bindings.insert(i.first, vAttr, i.second.pos);
} }
/* If the rec contains an attribute called `__overrides', then /* If the rec contains an attribute called `__overrides', then
@ -1272,32 +1267,28 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
been substituted into the bodies of the other attributes. been substituted into the bodies of the other attributes.
Hence we need __overrides.) */ Hence we need __overrides.) */
if (hasOverrides) { if (hasOverrides) {
Value * vOverrides = (*v.attrs)[overrides->second.displ].value; Value * vOverrides = (*bindings.bindings)[overrides->second.displ].value;
state.forceAttrs(*vOverrides, [&]() { return vOverrides->determinePos(noPos); }, "while evaluating the `__overrides` attribute"); state.forceAttrs(*vOverrides, [&]() { return vOverrides->determinePos(noPos); }, "while evaluating the `__overrides` attribute");
Bindings * newBnds = state.allocBindings(v.attrs->capacity() + vOverrides->attrs->size()); bindings.grow(state.allocBindings(bindings.capacity() + vOverrides->attrs()->size()));
for (auto & i : *v.attrs) for (auto & i : *vOverrides->attrs()) {
newBnds->push_back(i);
for (auto & i : *vOverrides->attrs) {
AttrDefs::iterator j = attrs.find(i.name); AttrDefs::iterator j = attrs.find(i.name);
if (j != attrs.end()) { if (j != attrs.end()) {
(*newBnds)[j->second.displ] = i; (*bindings.bindings)[j->second.displ] = i;
env2.values[j->second.displ] = i.value; env2.values[j->second.displ] = i.value;
} else } else
newBnds->push_back(i); bindings.push_back(i);
} }
newBnds->sort(); sort = true;
v.attrs = newBnds;
} }
} }
else { else {
Env * inheritEnv = inheritFromExprs ? buildInheritFromEnv(state, env) : nullptr; Env * inheritEnv = inheritFromExprs ? buildInheritFromEnv(state, env) : nullptr;
for (auto & i : attrs) { for (auto & i : attrs)
v.attrs->push_back(Attr( bindings.insert(
i.first, i.first,
i.second.e->maybeThunk(state, *i.second.chooseByKind(&env, &env, inheritEnv)), i.second.e->maybeThunk(state, *i.second.chooseByKind(&env, &env, inheritEnv)),
i.second.pos)); i.second.pos);
}
} }
/* Dynamic attrs apply *after* rec and __overrides. */ /* Dynamic attrs apply *after* rec and __overrides. */
@ -1309,17 +1300,21 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
continue; continue;
state.forceStringNoCtx(nameVal, i.pos, "while evaluating the name of a dynamic attribute"); state.forceStringNoCtx(nameVal, i.pos, "while evaluating the name of a dynamic attribute");
auto nameSym = state.symbols.create(nameVal.string_view()); auto nameSym = state.symbols.create(nameVal.string_view());
Bindings::iterator j = v.attrs->find(nameSym); if (sort)
if (j != v.attrs->end()) // FIXME: inefficient
bindings.bindings->sort();
if (auto j = bindings.bindings->get(nameSym))
state.error<EvalError>("dynamic attribute '%1%' already defined at %2%", state.symbols[nameSym], state.positions[j->pos]).atPos(i.pos).withFrame(env, *this).debugThrow(); state.error<EvalError>("dynamic attribute '%1%' already defined at %2%", state.symbols[nameSym], state.positions[j->pos]).atPos(i.pos).withFrame(env, *this).debugThrow();
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), i.pos)); bindings.insert(nameSym, i.valueExpr->maybeThunk(state, *dynamicEnv), i.pos);
v.attrs->sort(); // FIXME: inefficient sort = true;
} }
v.attrs->pos = pos; bindings.bindings->pos = pos;
v.mkAttrs(sort ? bindings.finish() : bindings.alreadySorted());
} }
@ -1425,21 +1420,21 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
for (auto & i : attrPath) { for (auto & i : attrPath) {
state.nrLookups++; state.nrLookups++;
Bindings::iterator j; const Attr * j;
auto name = getName(i, state, env); auto name = getName(i, state, env);
if (def) { if (def) {
state.forceValue(*vAttrs, pos); state.forceValue(*vAttrs, pos);
if (vAttrs->type() != nAttrs || if (vAttrs->type() != nAttrs ||
(j = vAttrs->attrs->find(name)) == vAttrs->attrs->end()) !(j = vAttrs->attrs()->get(name)))
{ {
def->eval(state, env, v); def->eval(state, env, v);
return; return;
} }
} else { } else {
state.forceAttrs(*vAttrs, pos, "while selecting an attribute"); state.forceAttrs(*vAttrs, pos, "while selecting an attribute");
if ((j = vAttrs->attrs->find(name)) == vAttrs->attrs->end()) { if (!(j = vAttrs->attrs()->get(name))) {
std::set<std::string> allAttrNames; std::set<std::string> allAttrNames;
for (auto & attr : *vAttrs->attrs) for (auto & attr : *vAttrs->attrs())
allAttrNames.insert(state.symbols[attr.name]); allAttrNames.insert(state.symbols[attr.name]);
auto suggestions = Suggestions::bestMatches(allAttrNames, state.symbols[name]); auto suggestions = Suggestions::bestMatches(allAttrNames, state.symbols[name]);
state.error<EvalError>("attribute '%1%' missing", state.symbols[name]) state.error<EvalError>("attribute '%1%' missing", state.symbols[name])
@ -1477,15 +1472,15 @@ void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v)
for (auto & i : attrPath) { for (auto & i : attrPath) {
state.forceValue(*vAttrs, getPos()); state.forceValue(*vAttrs, getPos());
Bindings::iterator j; const Attr * j;
auto name = getName(i, state, env); auto name = getName(i, state, env);
if (vAttrs->type() != nAttrs || if (vAttrs->type() == nAttrs &&
(j = vAttrs->attrs->find(name)) == vAttrs->attrs->end()) (j = vAttrs->attrs()->get(name)))
{ {
vAttrs = j->value;
} else {
v.mkBool(false); v.mkBool(false);
return; return;
} else {
vAttrs = j->value;
} }
} }
@ -1537,19 +1532,19 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
} }
}; };
Attr * functor; const Attr * functor;
while (nrArgs > 0) { while (nrArgs > 0) {
if (vCur.isLambda()) { if (vCur.isLambda()) {
ExprLambda & lambda(*vCur.lambda.fun); ExprLambda & lambda(*vCur.payload.lambda.fun);
auto size = auto size =
(!lambda.arg ? 0 : 1) + (!lambda.arg ? 0 : 1) +
(lambda.hasFormals() ? lambda.formals->formals.size() : 0); (lambda.hasFormals() ? lambda.formals->formals.size() : 0);
Env & env2(allocEnv(size)); Env & env2(allocEnv(size));
env2.up = vCur.lambda.env; env2.up = vCur.payload.lambda.env;
Displacement displ = 0; Displacement displ = 0;
@ -1571,7 +1566,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
argument has a default, use the default. */ argument has a default, use the default. */
size_t attrsUsed = 0; size_t attrsUsed = 0;
for (auto & i : lambda.formals->formals) { for (auto & i : lambda.formals->formals) {
auto j = args[0]->attrs->get(i.name); auto j = args[0]->attrs()->get(i.name);
if (!j) { if (!j) {
if (!i.def) { if (!i.def) {
error<TypeError>("function '%1%' called without required argument '%2%'", error<TypeError>("function '%1%' called without required argument '%2%'",
@ -1579,7 +1574,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
symbols[i.name]) symbols[i.name])
.atPos(lambda.pos) .atPos(lambda.pos)
.withTrace(pos, "from call site") .withTrace(pos, "from call site")
.withFrame(*fun.lambda.env, lambda) .withFrame(*fun.payload.lambda.env, lambda)
.debugThrow(); .debugThrow();
} }
env2.values[displ++] = i.def->maybeThunk(*this, env2); env2.values[displ++] = i.def->maybeThunk(*this, env2);
@ -1591,10 +1586,10 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
/* Check that each actual argument is listed as a formal /* Check that each actual argument is listed as a formal
argument (unless the attribute match specifies a `...'). */ argument (unless the attribute match specifies a `...'). */
if (!lambda.formals->ellipsis && attrsUsed != args[0]->attrs->size()) { if (!lambda.formals->ellipsis && attrsUsed != args[0]->attrs()->size()) {
/* Nope, so show the first unexpected argument to the /* Nope, so show the first unexpected argument to the
user. */ user. */
for (auto & i : *args[0]->attrs) for (auto & i : *args[0]->attrs())
if (!lambda.formals->has(i.name)) { if (!lambda.formals->has(i.name)) {
std::set<std::string> formalNames; std::set<std::string> formalNames;
for (auto & formal : lambda.formals->formals) for (auto & formal : lambda.formals->formals)
@ -1606,7 +1601,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
.atPos(lambda.pos) .atPos(lambda.pos)
.withTrace(pos, "from call site") .withTrace(pos, "from call site")
.withSuggestions(suggestions) .withSuggestions(suggestions)
.withFrame(*fun.lambda.env, lambda) .withFrame(*fun.payload.lambda.env, lambda)
.debugThrow(); .debugThrow();
} }
abort(); // can't happen abort(); // can't happen
@ -1648,7 +1643,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
else if (vCur.isPrimOp()) { else if (vCur.isPrimOp()) {
size_t argsLeft = vCur.primOp->arity; size_t argsLeft = vCur.primOp()->arity;
if (nrArgs < argsLeft) { if (nrArgs < argsLeft) {
/* We don't have enough arguments, so create a tPrimOpApp chain. */ /* We don't have enough arguments, so create a tPrimOpApp chain. */
@ -1656,7 +1651,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
return; return;
} else { } else {
/* We have all the arguments, so call the primop. */ /* We have all the arguments, so call the primop. */
auto * fn = vCur.primOp; auto * fn = vCur.primOp();
nrPrimOpCalls++; nrPrimOpCalls++;
if (countCalls) primOpCalls[fn->name]++; if (countCalls) primOpCalls[fn->name]++;
@ -1680,10 +1675,10 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
Value * primOp = &vCur; Value * primOp = &vCur;
while (primOp->isPrimOpApp()) { while (primOp->isPrimOpApp()) {
argsDone++; argsDone++;
primOp = primOp->primOpApp.left; primOp = primOp->payload.primOpApp.left;
} }
assert(primOp->isPrimOp()); assert(primOp->isPrimOp());
auto arity = primOp->primOp->arity; auto arity = primOp->primOp()->arity;
auto argsLeft = arity - argsDone; auto argsLeft = arity - argsDone;
if (nrArgs < argsLeft) { if (nrArgs < argsLeft) {
@ -1696,13 +1691,13 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
Value * vArgs[maxPrimOpArity]; Value * vArgs[maxPrimOpArity];
auto n = argsDone; auto n = argsDone;
for (Value * arg = &vCur; arg->isPrimOpApp(); arg = arg->primOpApp.left) for (Value * arg = &vCur; arg->isPrimOpApp(); arg = arg->payload.primOpApp.left)
vArgs[--n] = arg->primOpApp.right; vArgs[--n] = arg->payload.primOpApp.right;
for (size_t i = 0; i < argsLeft; ++i) for (size_t i = 0; i < argsLeft; ++i)
vArgs[argsDone + i] = args[i]; vArgs[argsDone + i] = args[i];
auto fn = primOp->primOp; auto fn = primOp->primOp();
nrPrimOpCalls++; nrPrimOpCalls++;
if (countCalls) primOpCalls[fn->name]++; if (countCalls) primOpCalls[fn->name]++;
@ -1723,7 +1718,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
} }
} }
else if (vCur.type() == nAttrs && (functor = vCur.attrs->get(sFunctor))) { else if (vCur.type() == nAttrs && (functor = vCur.attrs()->get(sFunctor))) {
/* 'vCur' may be allocated on the stack of the calling /* 'vCur' may be allocated on the stack of the calling
function, but for functors we may keep a reference, so function, but for functors we may keep a reference, so
heap-allocate a copy and use that instead. */ heap-allocate a copy and use that instead. */
@ -1798,8 +1793,8 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res)
forceValue(fun, pos); forceValue(fun, pos);
if (fun.type() == nAttrs) { if (fun.type() == nAttrs) {
auto found = fun.attrs->find(sFunctor); auto found = fun.attrs()->find(sFunctor);
if (found != fun.attrs->end()) { if (found != fun.attrs()->end()) {
Value * v = allocValue(); Value * v = allocValue();
callFunction(*found->value, fun, *v, pos); callFunction(*found->value, fun, *v, pos);
forceValue(*v, pos); forceValue(*v, pos);
@ -1807,14 +1802,14 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res)
} }
} }
if (!fun.isLambda() || !fun.lambda.fun->hasFormals()) { if (!fun.isLambda() || !fun.payload.lambda.fun->hasFormals()) {
res = fun; res = fun;
return; return;
} }
auto attrs = buildBindings(std::max(static_cast<uint32_t>(fun.lambda.fun->formals->formals.size()), args.size())); auto attrs = buildBindings(std::max(static_cast<uint32_t>(fun.payload.lambda.fun->formals->formals.size()), args.size()));
if (fun.lambda.fun->formals->ellipsis) { if (fun.payload.lambda.fun->formals->ellipsis) {
// If the formals have an ellipsis (eg the function accepts extra args) pass // If the formals have an ellipsis (eg the function accepts extra args) pass
// all available automatic arguments (which includes arguments specified on // all available automatic arguments (which includes arguments specified on
// the command line via --arg/--argstr) // the command line via --arg/--argstr)
@ -1822,9 +1817,9 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res)
attrs.insert(v); attrs.insert(v);
} else { } else {
// Otherwise, only pass the arguments that the function accepts // Otherwise, only pass the arguments that the function accepts
for (auto & i : fun.lambda.fun->formals->formals) { for (auto & i : fun.payload.lambda.fun->formals->formals) {
Bindings::iterator j = args.find(i.name); auto j = args.get(i.name);
if (j != args.end()) { if (j) {
attrs.insert(*j); attrs.insert(*j);
} else if (!i.def) { } else if (!i.def) {
error<MissingArgumentError>(R"(cannot evaluate a function that has an argument without a value ('%1%') error<MissingArgumentError>(R"(cannot evaluate a function that has an argument without a value ('%1%')
@ -1832,7 +1827,7 @@ Nix attempted to evaluate a function as a top level expression; in
this case it must have its arguments supplied either by default this case it must have its arguments supplied either by default
values, or passed explicitly with '--arg' or '--argstr'. See values, or passed explicitly with '--arg' or '--argstr'. See
https://nixos.org/manual/nix/stable/language/constructs.html#functions.)", symbols[i.name]) https://nixos.org/manual/nix/stable/language/constructs.html#functions.)", symbols[i.name])
.atPos(i.pos).withFrame(*fun.lambda.env, *fun.lambda.fun).debugThrow(); .atPos(i.pos).withFrame(*fun.payload.lambda.env, *fun.payload.lambda.fun).debugThrow();
} }
} }
} }
@ -1917,17 +1912,17 @@ void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v)
state.nrOpUpdates++; state.nrOpUpdates++;
if (v1.attrs->size() == 0) { v = v2; return; } if (v1.attrs()->size() == 0) { v = v2; return; }
if (v2.attrs->size() == 0) { v = v1; return; } if (v2.attrs()->size() == 0) { v = v1; return; }
auto attrs = state.buildBindings(v1.attrs->size() + v2.attrs->size()); auto attrs = state.buildBindings(v1.attrs()->size() + v2.attrs()->size());
/* Merge the sets, preferring values from the second set. Make /* Merge the sets, preferring values from the second set. Make
sure to keep the resulting vector in sorted order. */ sure to keep the resulting vector in sorted order. */
Bindings::iterator i = v1.attrs->begin(); auto i = v1.attrs()->begin();
Bindings::iterator j = v2.attrs->begin(); auto j = v2.attrs()->begin();
while (i != v1.attrs->end() && j != v2.attrs->end()) { while (i != v1.attrs()->end() && j != v2.attrs()->end()) {
if (i->name == j->name) { if (i->name == j->name) {
attrs.insert(*j); attrs.insert(*j);
++i; ++j; ++i; ++j;
@ -1938,12 +1933,12 @@ void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v)
attrs.insert(*j++); attrs.insert(*j++);
} }
while (i != v1.attrs->end()) attrs.insert(*i++); while (i != v1.attrs()->end()) attrs.insert(*i++);
while (j != v2.attrs->end()) attrs.insert(*j++); while (j != v2.attrs()->end()) attrs.insert(*j++);
v.mkAttrs(attrs.alreadySorted()); v.mkAttrs(attrs.alreadySorted());
state.nrOpUpdateValuesCopied += v.attrs->size(); state.nrOpUpdateValuesCopied += v.attrs()->size();
} }
@ -2035,19 +2030,19 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
if (firstType == nInt) { if (firstType == nInt) {
if (vTmp.type() == nInt) { if (vTmp.type() == nInt) {
n += vTmp.integer; n += vTmp.integer();
} else if (vTmp.type() == nFloat) { } else if (vTmp.type() == nFloat) {
// Upgrade the type from int to float; // Upgrade the type from int to float;
firstType = nFloat; firstType = nFloat;
nf = n; nf = n;
nf += vTmp.fpoint; nf += vTmp.fpoint();
} else } else
state.error<EvalError>("cannot add %1% to an integer", showType(vTmp)).atPos(i_pos).withFrame(env, *this).debugThrow(); state.error<EvalError>("cannot add %1% to an integer", showType(vTmp)).atPos(i_pos).withFrame(env, *this).debugThrow();
} else if (firstType == nFloat) { } else if (firstType == nFloat) {
if (vTmp.type() == nInt) { if (vTmp.type() == nInt) {
nf += vTmp.integer; nf += vTmp.integer();
} else if (vTmp.type() == nFloat) { } else if (vTmp.type() == nFloat) {
nf += vTmp.fpoint; nf += vTmp.fpoint();
} else } else
state.error<EvalError>("cannot add %1% to a float", showType(vTmp)).atPos(i_pos).withFrame(env, *this).debugThrow(); state.error<EvalError>("cannot add %1% to a float", showType(vTmp)).atPos(i_pos).withFrame(env, *this).debugThrow();
} else { } else {
@ -2120,11 +2115,11 @@ void EvalState::forceValueDeep(Value & v)
forceValue(v, v.determinePos(noPos)); forceValue(v, v.determinePos(noPos));
if (v.type() == nAttrs) { if (v.type() == nAttrs) {
for (auto & i : *v.attrs) for (auto & i : *v.attrs())
try { try {
// If the value is a thunk, we're evaling. Otherwise no trace necessary. // If the value is a thunk, we're evaling. Otherwise no trace necessary.
auto dts = debugRepl && i.value->isThunk() auto dts = debugRepl && i.value->isThunk()
? makeDebugTraceStacker(*this, *i.value->thunk.expr, *i.value->thunk.env, positions[i.pos], ? makeDebugTraceStacker(*this, *i.value->payload.thunk.expr, *i.value->payload.thunk.env, positions[i.pos],
"while evaluating the attribute '%1%'", symbols[i.name]) "while evaluating the attribute '%1%'", symbols[i.name])
: nullptr; : nullptr;
@ -2155,13 +2150,13 @@ NixInt EvalState::forceInt(Value & v, const PosIdx pos, std::string_view errorCt
showType(v), showType(v),
ValuePrinter(*this, v, errorPrintOptions) ValuePrinter(*this, v, errorPrintOptions)
).atPos(pos).debugThrow(); ).atPos(pos).debugThrow();
return v.integer; return v.integer();
} catch (Error & e) { } catch (Error & e) {
e.addTrace(positions[pos], errorCtx); e.addTrace(positions[pos], errorCtx);
throw; throw;
} }
return v.integer; return v.integer();
} }
@ -2170,14 +2165,14 @@ NixFloat EvalState::forceFloat(Value & v, const PosIdx pos, std::string_view err
try { try {
forceValue(v, pos); forceValue(v, pos);
if (v.type() == nInt) if (v.type() == nInt)
return v.integer; return v.integer();
else if (v.type() != nFloat) else if (v.type() != nFloat)
error<TypeError>( error<TypeError>(
"expected a float but found %1%: %2%", "expected a float but found %1%: %2%",
showType(v), showType(v),
ValuePrinter(*this, v, errorPrintOptions) ValuePrinter(*this, v, errorPrintOptions)
).atPos(pos).debugThrow(); ).atPos(pos).debugThrow();
return v.fpoint; return v.fpoint();
} catch (Error & e) { } catch (Error & e) {
e.addTrace(positions[pos], errorCtx); e.addTrace(positions[pos], errorCtx);
throw; throw;
@ -2195,19 +2190,19 @@ bool EvalState::forceBool(Value & v, const PosIdx pos, std::string_view errorCtx
showType(v), showType(v),
ValuePrinter(*this, v, errorPrintOptions) ValuePrinter(*this, v, errorPrintOptions)
).atPos(pos).debugThrow(); ).atPos(pos).debugThrow();
return v.boolean; return v.boolean();
} catch (Error & e) { } catch (Error & e) {
e.addTrace(positions[pos], errorCtx); e.addTrace(positions[pos], errorCtx);
throw; throw;
} }
return v.boolean; return v.boolean();
} }
bool EvalState::isFunctor(Value & fun) bool EvalState::isFunctor(Value & fun)
{ {
return fun.type() == nAttrs && fun.attrs->find(sFunctor) != fun.attrs->end(); return fun.type() == nAttrs && fun.attrs()->find(sFunctor) != fun.attrs()->end();
} }
@ -2248,8 +2243,8 @@ std::string_view EvalState::forceString(Value & v, const PosIdx pos, std::string
void copyContext(const Value & v, NixStringContext & context) void copyContext(const Value & v, NixStringContext & context)
{ {
if (v.string.context) if (v.payload.string.context)
for (const char * * p = v.string.context; *p; ++p) for (const char * * p = v.payload.string.context; *p; ++p)
context.insert(NixStringContextElem::parse(*p)); context.insert(NixStringContextElem::parse(*p));
} }
@ -2275,8 +2270,8 @@ std::string_view EvalState::forceStringNoCtx(Value & v, const PosIdx pos, std::s
bool EvalState::isDerivation(Value & v) bool EvalState::isDerivation(Value & v)
{ {
if (v.type() != nAttrs) return false; if (v.type() != nAttrs) return false;
Bindings::iterator i = v.attrs->find(sType); auto i = v.attrs()->get(sType);
if (i == v.attrs->end()) return false; if (!i) 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 i->value->string_view().compare("derivation") == 0; return i->value->string_view().compare("derivation") == 0;
@ -2286,8 +2281,8 @@ bool EvalState::isDerivation(Value & v)
std::optional<std::string> EvalState::tryAttrsToString(const PosIdx pos, Value & v, std::optional<std::string> EvalState::tryAttrsToString(const PosIdx pos, Value & v,
NixStringContext & context, bool coerceMore, bool copyToStore) NixStringContext & context, bool coerceMore, bool copyToStore)
{ {
auto i = v.attrs->find(sToString); auto i = v.attrs()->find(sToString);
if (i != v.attrs->end()) { if (i != v.attrs()->end()) {
Value v1; Value v1;
callFunction(*i->value, v, v1, pos); callFunction(*i->value, v, v1, pos);
return coerceToString(pos, v1, context, return coerceToString(pos, v1, context,
@ -2319,7 +2314,7 @@ BackedStringView EvalState::coerceToString(
!canonicalizePath && !copyToStore !canonicalizePath && !copyToStore
? // FIXME: hack to preserve path literals that end in a ? // FIXME: hack to preserve path literals that end in a
// slash, as in /foo/${x}. // slash, as in /foo/${x}.
v._path.path v.payload.path.path
: copyToStore : copyToStore
? store->printStorePath(copyPathToStore(context, v.path())) ? store->printStorePath(copyPathToStore(context, v.path()))
: std::string(v.path().path.abs()); : std::string(v.path().path.abs());
@ -2329,8 +2324,8 @@ BackedStringView EvalState::coerceToString(
auto maybeString = tryAttrsToString(pos, v, context, coerceMore, copyToStore); auto maybeString = tryAttrsToString(pos, v, context, coerceMore, copyToStore);
if (maybeString) if (maybeString)
return std::move(*maybeString); return std::move(*maybeString);
auto i = v.attrs->find(sOutPath); auto i = v.attrs()->find(sOutPath);
if (i == v.attrs->end()) { if (i == v.attrs()->end()) {
error<TypeError>( error<TypeError>(
"cannot coerce %1% to a string: %2%", "cannot coerce %1% to a string: %2%",
showType(v), showType(v),
@ -2345,7 +2340,7 @@ BackedStringView EvalState::coerceToString(
if (v.type() == nExternal) { if (v.type() == nExternal) {
try { try {
return v.external->coerceToString(*this, pos, context, coerceMore, copyToStore); return v.external()->coerceToString(*this, pos, context, coerceMore, copyToStore);
} catch (Error & e) { } catch (Error & e) {
e.addTrace(nullptr, errorCtx); e.addTrace(nullptr, errorCtx);
throw; throw;
@ -2355,10 +2350,10 @@ BackedStringView EvalState::coerceToString(
if (coerceMore) { if (coerceMore) {
/* Note that `false' is represented as an empty string for /* Note that `false' is represented as an empty string for
shell scripting convenience, just like `null'. */ shell scripting convenience, just like `null'. */
if (v.type() == nBool && v.boolean) return "1"; if (v.type() == nBool && v.boolean()) return "1";
if (v.type() == nBool && !v.boolean) return ""; if (v.type() == nBool && !v.boolean()) return "";
if (v.type() == nInt) return std::to_string(v.integer); if (v.type() == nInt) return std::to_string(v.integer());
if (v.type() == nFloat) return std::to_string(v.fpoint); if (v.type() == nFloat) return std::to_string(v.fpoint());
if (v.type() == nNull) return ""; if (v.type() == nNull) return "";
if (v.isList()) { if (v.isList()) {
@ -2437,8 +2432,8 @@ SourcePath EvalState::coerceToPath(const PosIdx pos, Value & v, NixStringContext
/* Similarly, handle __toString where the result may be a path /* Similarly, handle __toString where the result may be a path
value. */ value. */
if (v.type() == nAttrs) { if (v.type() == nAttrs) {
auto i = v.attrs->find(sToString); auto i = v.attrs()->find(sToString);
if (i != v.attrs->end()) { if (i != v.attrs()->end()) {
Value v1; Value v1;
callFunction(*i->value, v, v1, pos); callFunction(*i->value, v, v1, pos);
return coerceToPath(pos, v1, context, errorCtx); return coerceToPath(pos, v1, context, errorCtx);
@ -2532,19 +2527,19 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v
// Special case type-compatibility between float and int // Special case type-compatibility between float and int
if (v1.type() == nInt && v2.type() == nFloat) if (v1.type() == nInt && v2.type() == nFloat)
return v1.integer == v2.fpoint; return v1.integer() == v2.fpoint();
if (v1.type() == nFloat && v2.type() == nInt) if (v1.type() == nFloat && v2.type() == nInt)
return v1.fpoint == v2.integer; return v1.fpoint() == v2.integer();
// All other types are not compatible with each other. // All other types are not compatible with each other.
if (v1.type() != v2.type()) return false; if (v1.type() != v2.type()) return false;
switch (v1.type()) { switch (v1.type()) {
case nInt: case nInt:
return v1.integer == v2.integer; return v1.integer() == v2.integer();
case nBool: case nBool:
return v1.boolean == v2.boolean; return v1.boolean() == v2.boolean();
case nString: case nString:
return strcmp(v1.c_str(), v2.c_str()) == 0; return strcmp(v1.c_str(), v2.c_str()) == 0;
@ -2552,8 +2547,8 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v
case nPath: case nPath:
return return
// FIXME: compare accessors by their fingerprint. // FIXME: compare accessors by their fingerprint.
v1._path.accessor == v2._path.accessor v1.payload.path.accessor == v2.payload.path.accessor
&& strcmp(v1._path.path, v2._path.path) == 0; && strcmp(v1.payload.path.path, v2.payload.path.path) == 0;
case nNull: case nNull:
return true; return true;
@ -2568,17 +2563,17 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v
/* If both sets denote a derivation (type = "derivation"), /* If both sets denote a derivation (type = "derivation"),
then compare their outPaths. */ then compare their outPaths. */
if (isDerivation(v1) && isDerivation(v2)) { if (isDerivation(v1) && isDerivation(v2)) {
Bindings::iterator i = v1.attrs->find(sOutPath); auto i = v1.attrs()->get(sOutPath);
Bindings::iterator j = v2.attrs->find(sOutPath); auto j = v2.attrs()->get(sOutPath);
if (i != v1.attrs->end() && j != v2.attrs->end()) if (i && j)
return eqValues(*i->value, *j->value, pos, errorCtx); return eqValues(*i->value, *j->value, pos, errorCtx);
} }
if (v1.attrs->size() != v2.attrs->size()) return false; if (v1.attrs()->size() != v2.attrs()->size()) return false;
/* Otherwise, compare the attributes one by one. */ /* Otherwise, compare the attributes one by one. */
Bindings::iterator i, j; Bindings::const_iterator i, j;
for (i = v1.attrs->begin(), j = v2.attrs->begin(); i != v1.attrs->end(); ++i, ++j) for (i = v1.attrs()->begin(), j = v2.attrs()->begin(); i != v1.attrs()->end(); ++i, ++j)
if (i->name != j->name || !eqValues(*i->value, *j->value, pos, errorCtx)) if (i->name != j->name || !eqValues(*i->value, *j->value, pos, errorCtx))
return false; return false;
@ -2590,10 +2585,10 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v
return false; return false;
case nExternal: case nExternal:
return *v1.external == *v2.external; return *v1.external() == *v2.external();
case nFloat: case nFloat:
return v1.fpoint == v2.fpoint; return v1.fpoint() == v2.fpoint();
case nThunk: // Must not be left by forceValue case nThunk: // Must not be left by forceValue
default: default:

View file

@ -94,7 +94,7 @@ struct PrimOp
void check(); void check();
}; };
std::ostream & operator<<(std::ostream & output, PrimOp & primOp); std::ostream & operator<<(std::ostream & output, const PrimOp & primOp);
/** /**
* Info about a constant * Info about a constant
@ -643,9 +643,6 @@ public:
inline Value * allocValue(); inline Value * allocValue();
inline Env & allocEnv(size_t size); inline Env & allocEnv(size_t size);
Value * allocAttr(Value & vAttrs, Symbol name);
Value * allocAttr(Value & vAttrs, std::string_view name);
Bindings * allocBindings(size_t capacity); Bindings * allocBindings(size_t capacity);
BindingsBuilder buildBindings(size_t capacity) BindingsBuilder buildBindings(size_t capacity)

View file

@ -112,7 +112,7 @@ static FlakeInput parseFlakeInput(EvalState & state,
fetchers::Attrs attrs; fetchers::Attrs attrs;
std::optional<std::string> url; std::optional<std::string> url;
for (nix::Attr attr : *(value->attrs)) { for (auto & 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);
@ -120,7 +120,7 @@ static FlakeInput parseFlakeInput(EvalState & state,
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) {
@ -137,10 +137,10 @@ static FlakeInput parseFlakeInput(EvalState & state,
attrs.emplace(state.symbols[attr.name], attr.value->c_str()); attrs.emplace(state.symbols[attr.name], attr.value->c_str());
break; break;
case nBool: case nBool:
attrs.emplace(state.symbols[attr.name], Explicit<bool> { attr.value->boolean }); attrs.emplace(state.symbols[attr.name], Explicit<bool> { attr.value->boolean() });
break; break;
case nInt: case nInt:
attrs.emplace(state.symbols[attr.name], (long unsigned int) attr.value->integer); attrs.emplace(state.symbols[attr.name], (long unsigned int) attr.value->integer());
break; break;
default: default:
if (attr.name == state.symbols.create("publicKeys")) { if (attr.name == state.symbols.create("publicKeys")) {
@ -190,7 +190,7 @@ static std::map<FlakeId, FlakeInput> parseFlakeInputs(
expectType(state, nAttrs, *value, pos); expectType(state, nAttrs, *value, pos);
for (nix::Attr & inputAttr : *(*value).attrs) { for (auto & inputAttr : *value->attrs()) {
inputs.emplace(state.symbols[inputAttr.name], inputs.emplace(state.symbols[inputAttr.name],
parseFlakeInput(state, parseFlakeInput(state,
state.symbols[inputAttr.name], state.symbols[inputAttr.name],
@ -224,23 +224,23 @@ static Flake readFlake(
.path = flakePath, .path = flakePath,
}; };
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->c_str(); flake.description = description->value->c_str();
} }
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, flakePath.parent().path.abs(), lockRootPath); // FIXME flake.inputs = parseFlakeInputs(state, inputs->value, inputs->pos, flakePath.parent().path.abs(), lockRootPath); // FIXME
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->payload.lambda.fun->hasFormals()) {
for (auto & formal : outputs->value->lambda.fun->formals->formals) { for (auto & formal : outputs->value->payload.lambda.fun->formals->formals) {
if (formal.name != state.sSelf) if (formal.name != state.sSelf)
flake.inputs.emplace(state.symbols[formal.name], FlakeInput { flake.inputs.emplace(state.symbols[formal.name], FlakeInput {
.ref = parseFlakeRef(state.symbols[formal.name]) .ref = parseFlakeRef(state.symbols[formal.name])
@ -253,10 +253,10 @@ static Flake readFlake(
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.emplace( flake.config.settings.emplace(
@ -292,7 +292,7 @@ static Flake readFlake(
} }
} }
for (auto & attr : *vInfo.attrs) { for (auto & attr : *vInfo.attrs()) {
if (attr.name != state.sDescription && if (attr.name != state.sDescription &&
attr.name != sInputs && attr.name != sInputs &&
attr.name != sOutputs && attr.name != sOutputs &&
@ -893,14 +893,14 @@ static void prim_flakeRefToString(
state.forceAttrs(*args[0], noPos, state.forceAttrs(*args[0], noPos,
"while evaluating the argument passed to builtins.flakeRefToString"); "while evaluating the argument passed to builtins.flakeRefToString");
fetchers::Attrs attrs; fetchers::Attrs attrs;
for (const auto & attr : *args[0]->attrs) { for (const auto & attr : *args[0]->attrs()) {
auto t = attr.value->type(); auto t = attr.value->type();
if (t == nInt) { if (t == nInt) {
attrs.emplace(state.symbols[attr.name], attrs.emplace(state.symbols[attr.name],
(uint64_t) attr.value->integer); (uint64_t) attr.value->integer());
} else if (t == nBool) { } else if (t == nBool) {
attrs.emplace(state.symbols[attr.name], attrs.emplace(state.symbols[attr.name],
Explicit<bool> { attr.value->boolean }); Explicit<bool> { attr.value->boolean() });
} else if (t == nString) { } else if (t == nString) {
attrs.emplace(state.symbols[attr.name], attrs.emplace(state.symbols[attr.name],
std::string(attr.value->string_view())); std::string(attr.value->string_view()));

View file

@ -11,7 +11,7 @@
namespace nix { namespace nix {
PackageInfo::PackageInfo(EvalState & state, std::string attrPath, Bindings * attrs) PackageInfo::PackageInfo(EvalState & state, std::string attrPath, const Bindings * attrs)
: state(&state), attrs(attrs), attrPath(std::move(attrPath)) : state(&state), attrs(attrs), attrPath(std::move(attrPath))
{ {
} }
@ -69,12 +69,11 @@ std::string PackageInfo::querySystem() const
std::optional<StorePath> PackageInfo::queryDrvPath() const std::optional<StorePath> PackageInfo::queryDrvPath() const
{ {
if (!drvPath && attrs) { if (!drvPath && attrs) {
Bindings::iterator i = attrs->find(state->sDrvPath);
NixStringContext context; NixStringContext context;
if (i == attrs->end()) if (auto i = attrs->get(state->sDrvPath))
drvPath = {std::nullopt};
else
drvPath = {state->coerceToStorePath(i->pos, *i->value, context, "while evaluating the 'drvPath' attribute of a derivation")}; drvPath = {state->coerceToStorePath(i->pos, *i->value, context, "while evaluating the 'drvPath' attribute of a derivation")};
else
drvPath = {std::nullopt};
} }
return drvPath.value_or(std::nullopt); return drvPath.value_or(std::nullopt);
} }
@ -91,7 +90,7 @@ StorePath PackageInfo::requireDrvPath() const
StorePath PackageInfo::queryOutPath() const StorePath PackageInfo::queryOutPath() const
{ {
if (!outPath && attrs) { if (!outPath && attrs) {
Bindings::iterator i = attrs->find(state->sOutPath); auto i = attrs->find(state->sOutPath);
NixStringContext context; NixStringContext context;
if (i != attrs->end()) if (i != attrs->end())
outPath = state->coerceToStorePath(i->pos, *i->value, context, "while evaluating the output path of a derivation"); outPath = state->coerceToStorePath(i->pos, *i->value, context, "while evaluating the output path of a derivation");
@ -106,8 +105,8 @@ PackageInfo::Outputs PackageInfo::queryOutputs(bool withPaths, bool onlyOutputsT
{ {
if (outputs.empty()) { if (outputs.empty()) {
/* Get the outputs list. */ /* Get the outputs list. */
Bindings::iterator i; const Attr * i;
if (attrs && (i = attrs->find(state->sOutputs)) != attrs->end()) { if (attrs && (i = attrs->get(state->sOutputs))) {
state->forceList(*i->value, i->pos, "while evaluating the 'outputs' attribute of a derivation"); state->forceList(*i->value, i->pos, "while evaluating the 'outputs' attribute of a derivation");
/* For each output... */ /* For each output... */
@ -116,13 +115,13 @@ PackageInfo::Outputs PackageInfo::queryOutputs(bool withPaths, bool onlyOutputsT
if (withPaths) { if (withPaths) {
/* Evaluate the corresponding set. */ /* Evaluate the corresponding set. */
Bindings::iterator out = attrs->find(state->symbols.create(output)); auto out = attrs->get(state->symbols.create(output));
if (out == attrs->end()) continue; // FIXME: throw error? if (!out) continue; // FIXME: throw error?
state->forceAttrs(*out->value, i->pos, "while evaluating an output of a derivation"); state->forceAttrs(*out->value, i->pos, "while evaluating an output of a derivation");
/* And evaluate its outPath attribute. */ /* And evaluate its outPath attribute. */
Bindings::iterator outPath = out->value->attrs->find(state->sOutPath); auto outPath = out->value->attrs()->get(state->sOutPath);
if (outPath == out->value->attrs->end()) continue; // FIXME: throw error? if (!outPath) continue; // FIXME: throw error?
NixStringContext context; NixStringContext context;
outputs.emplace(output, state->coerceToStorePath(outPath->pos, *outPath->value, context, "while evaluating an output path of a derivation")); outputs.emplace(output, state->coerceToStorePath(outPath->pos, *outPath->value, context, "while evaluating an output path of a derivation"));
} else } else
@ -135,8 +134,8 @@ PackageInfo::Outputs PackageInfo::queryOutputs(bool withPaths, bool onlyOutputsT
if (!onlyOutputsToInstall || !attrs) if (!onlyOutputsToInstall || !attrs)
return outputs; return outputs;
Bindings::iterator i; const Attr * i;
if (attrs && (i = attrs->find(state->sOutputSpecified)) != attrs->end() && state->forceBool(*i->value, i->pos, "while evaluating the 'outputSpecified' attribute of a derivation")) { if (attrs && (i = attrs->get(state->sOutputSpecified)) && state->forceBool(*i->value, i->pos, "while evaluating the 'outputSpecified' attribute of a derivation")) {
Outputs result; Outputs result;
auto out = outputs.find(queryOutputName()); auto out = outputs.find(queryOutputName());
if (out == outputs.end()) if (out == outputs.end())
@ -167,21 +166,21 @@ PackageInfo::Outputs PackageInfo::queryOutputs(bool withPaths, bool onlyOutputsT
std::string PackageInfo::queryOutputName() const std::string PackageInfo::queryOutputName() const
{ {
if (outputName == "" && attrs) { if (outputName == "" && attrs) {
Bindings::iterator i = attrs->find(state->sOutputName); auto i = attrs->get(state->sOutputName);
outputName = i != attrs->end() ? state->forceStringNoCtx(*i->value, noPos, "while evaluating the output name of a derivation") : ""; outputName = i ? state->forceStringNoCtx(*i->value, noPos, "while evaluating the output name of a derivation") : "";
} }
return outputName; return outputName;
} }
Bindings * PackageInfo::getMeta() const Bindings * PackageInfo::getMeta()
{ {
if (meta) return meta; if (meta) return meta;
if (!attrs) return 0; if (!attrs) return 0;
Bindings::iterator a = attrs->find(state->sMeta); auto a = attrs->get(state->sMeta);
if (a == attrs->end()) return 0; if (!a) return 0;
state->forceAttrs(*a->value, a->pos, "while evaluating the 'meta' attribute of a derivation"); state->forceAttrs(*a->value, a->pos, "while evaluating the 'meta' attribute of a derivation");
meta = a->value->attrs; meta = a->value->attrs();
return meta; return meta;
} }
@ -205,9 +204,8 @@ bool PackageInfo::checkMeta(Value & v)
return true; return true;
} }
else if (v.type() == nAttrs) { else if (v.type() == nAttrs) {
Bindings::iterator i = v.attrs->find(state->sOutPath); if (v.attrs()->get(state->sOutPath)) return false;
if (i != v.attrs->end()) return false; for (auto & i : *v.attrs())
for (auto & i : *v.attrs)
if (!checkMeta(*i.value)) return false; if (!checkMeta(*i.value)) return false;
return true; return true;
} }
@ -219,8 +217,8 @@ bool PackageInfo::checkMeta(Value & v)
Value * PackageInfo::queryMeta(const std::string & name) Value * PackageInfo::queryMeta(const std::string & name)
{ {
if (!getMeta()) return 0; if (!getMeta()) return 0;
Bindings::iterator a = meta->find(state->symbols.create(name)); auto a = meta->get(state->symbols.create(name));
if (a == meta->end() || !checkMeta(*a->value)) return 0; if (!a || !checkMeta(*a->value)) return 0;
return a->value; return a->value;
} }
@ -237,7 +235,7 @@ NixInt PackageInfo::queryMetaInt(const std::string & name, NixInt def)
{ {
Value * v = queryMeta(name); Value * v = queryMeta(name);
if (!v) return def; if (!v) return def;
if (v->type() == nInt) return v->integer; if (v->type() == nInt) return v->integer();
if (v->type() == nString) { if (v->type() == nString) {
/* Backwards compatibility with before we had support for /* Backwards compatibility with before we had support for
integer meta fields. */ integer meta fields. */
@ -251,7 +249,7 @@ NixFloat PackageInfo::queryMetaFloat(const std::string & name, NixFloat def)
{ {
Value * v = queryMeta(name); Value * v = queryMeta(name);
if (!v) return def; if (!v) return def;
if (v->type() == nFloat) return v->fpoint; if (v->type() == nFloat) return v->fpoint();
if (v->type() == nString) { if (v->type() == nString) {
/* Backwards compatibility with before we had support for /* Backwards compatibility with before we had support for
float meta fields. */ float meta fields. */
@ -266,7 +264,7 @@ bool PackageInfo::queryMetaBool(const std::string & name, bool def)
{ {
Value * v = queryMeta(name); Value * v = queryMeta(name);
if (!v) return def; if (!v) return def;
if (v->type() == nBool) return v->boolean; if (v->type() == nBool) return v->boolean();
if (v->type() == nString) { if (v->type() == nString) {
/* Backwards compatibility with before we had support for /* Backwards compatibility with before we had support for
Boolean meta fields. */ Boolean meta fields. */
@ -292,7 +290,7 @@ void PackageInfo::setMeta(const std::string & name, Value * v)
/* Cache for already considered attrsets. */ /* Cache for already considered attrsets. */
typedef std::set<Bindings *> Done; typedef std::set<const Bindings *> Done;
/* Evaluate value `v'. If it evaluates to a set of type `derivation', /* Evaluate value `v'. If it evaluates to a set of type `derivation',
@ -309,9 +307,9 @@ static bool getDerivation(EvalState & state, Value & v,
/* Remove spurious duplicates (e.g., a set like `rec { x = /* Remove spurious duplicates (e.g., a set like `rec { x =
derivation {...}; y = x;}'. */ derivation {...}; y = x;}'. */
if (!done.insert(v.attrs).second) return false; if (!done.insert(v.attrs()).second) return false;
PackageInfo drv(state, attrPath, v.attrs); PackageInfo drv(state, attrPath, v.attrs());
drv.queryName(); drv.queryName();
@ -361,14 +359,14 @@ static void getDerivations(EvalState & state, Value & vIn,
/* !!! undocumented hackery to support combining channels in /* !!! undocumented hackery to support combining channels in
nix-env.cc. */ nix-env.cc. */
bool combineChannels = v.attrs->find(state.symbols.create("_combineChannels")) != v.attrs->end(); bool combineChannels = v.attrs()->get(state.symbols.create("_combineChannels"));
/* Consider the attributes in sorted order to get more /* Consider the attributes in sorted order to get more
deterministic behaviour in nix-env operations (e.g. when deterministic behaviour in nix-env operations (e.g. when
there are names clashes between derivations, the derivation there are names clashes between derivations, the derivation
bound to the attribute with the "lower" name should take bound to the attribute with the "lower" name should take
precedence). */ precedence). */
for (auto & i : v.attrs->lexicographicOrder(state.symbols)) { for (auto & i : v.attrs()->lexicographicOrder(state.symbols)) {
debug("evaluating attribute '%1%'", state.symbols[i->name]); debug("evaluating attribute '%1%'", state.symbols[i->name]);
if (!std::regex_match(std::string(state.symbols[i->name]), attrRegex)) if (!std::regex_match(std::string(state.symbols[i->name]), attrRegex))
continue; continue;
@ -380,8 +378,8 @@ static void getDerivations(EvalState & state, Value & vIn,
should we recurse into it? => Only if it has a should we recurse into it? => Only if it has a
`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); auto j = i->value->attrs()->get(state.sRecurseForDerivations);
if (j != i->value->attrs->end() && state.forceBool(*j->value, j->pos, "while evaluating the attribute `recurseForDerivations`")) if (j && state.forceBool(*j->value, j->pos, "while evaluating the attribute `recurseForDerivations`"))
getDerivations(state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures); getDerivations(state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
} }
} }

View file

@ -33,9 +33,9 @@ private:
*/ */
bool failed = false; bool failed = false;
Bindings * attrs = nullptr, * meta = nullptr; const Bindings * attrs = nullptr, * meta = nullptr;
Bindings * getMeta(); const Bindings * getMeta();
bool checkMeta(Value & v); bool checkMeta(Value & v);
@ -46,7 +46,7 @@ public:
std::string attrPath; std::string attrPath;
PackageInfo(EvalState & state) : state(&state) { }; PackageInfo(EvalState & state) : state(&state) { };
PackageInfo(EvalState & state, std::string attrPath, Bindings * attrs); PackageInfo(EvalState & state, std::string attrPath, const Bindings * attrs);
PackageInfo(EvalState & state, ref<Store> store, const std::string & drvPathWithOutputs); PackageInfo(EvalState & state, ref<Store> store, const std::string & drvPathWithOutputs);
std::string queryName() const; std::string queryName() const;

View file

@ -28,12 +28,12 @@ void Expr::show(const SymbolTable & symbols, std::ostream & str) const
void ExprInt::show(const SymbolTable & symbols, std::ostream & str) const void ExprInt::show(const SymbolTable & symbols, std::ostream & str) const
{ {
str << v.integer; str << v.integer();
} }
void ExprFloat::show(const SymbolTable & symbols, std::ostream & str) const void ExprFloat::show(const SymbolTable & symbols, std::ostream & str) const
{ {
str << v.fpoint; str << v.fpoint();
} }
void ExprString::show(const SymbolTable & symbols, std::ostream & str) const void ExprString::show(const SymbolTable & symbols, std::ostream & str) const

View file

@ -223,13 +223,13 @@ static void import(EvalState & state, const PosIdx pos, Value & vPath, Value * v
else { else {
state.forceAttrs(*vScope, pos, "while evaluating the first argument passed to builtins.scopedImport"); state.forceAttrs(*vScope, pos, "while evaluating the first argument passed to builtins.scopedImport");
Env * env = &state.allocEnv(vScope->attrs->size()); Env * env = &state.allocEnv(vScope->attrs()->size());
env->up = &state.baseEnv; env->up = &state.baseEnv;
auto staticEnv = std::make_shared<StaticEnv>(nullptr, state.staticBaseEnv.get(), vScope->attrs->size()); auto staticEnv = std::make_shared<StaticEnv>(nullptr, state.staticBaseEnv.get(), vScope->attrs()->size());
unsigned int displ = 0; unsigned int displ = 0;
for (auto & attr : *vScope->attrs) { for (auto & attr : *vScope->attrs()) {
staticEnv->vars.emplace_back(attr.name, displ); staticEnv->vars.emplace_back(attr.name, displ);
env->values[displ++] = attr.value; env->values[displ++] = attr.value;
} }
@ -418,7 +418,7 @@ static void prim_typeOf(EvalState & state, const PosIdx pos, Value * * args, Val
case nList: t = "list"; break; case nList: t = "list"; break;
case nFunction: t = "lambda"; break; case nFunction: t = "lambda"; break;
case nExternal: case nExternal:
t = args[0]->external->typeOf(); t = args[0]->external()->typeOf();
break; break;
case nFloat: t = "float"; break; case nFloat: t = "float"; break;
case nThunk: abort(); case nThunk: abort();
@ -582,9 +582,9 @@ struct CompareValues
{ {
try { try {
if (v1->type() == nFloat && v2->type() == nInt) if (v1->type() == nFloat && v2->type() == nInt)
return v1->fpoint < v2->integer; return v1->fpoint() < v2->integer();
if (v1->type() == nInt && v2->type() == nFloat) if (v1->type() == nInt && v2->type() == nFloat)
return v1->integer < v2->fpoint; return v1->integer() < v2->fpoint();
if (v1->type() != v2->type()) if (v1->type() != v2->type())
state.error<EvalError>("cannot compare %s with %s", showType(*v1), showType(*v2)).debugThrow(); state.error<EvalError>("cannot compare %s with %s", showType(*v1), showType(*v2)).debugThrow();
// Allow selecting a subset of enum values // Allow selecting a subset of enum values
@ -592,16 +592,16 @@ struct CompareValues
#pragma GCC diagnostic ignored "-Wswitch-enum" #pragma GCC diagnostic ignored "-Wswitch-enum"
switch (v1->type()) { switch (v1->type()) {
case nInt: case nInt:
return v1->integer < v2->integer; return v1->integer() < v2->integer();
case nFloat: case nFloat:
return v1->fpoint < v2->fpoint; return v1->fpoint() < v2->fpoint();
case nString: case nString:
return strcmp(v1->c_str(), v2->c_str()) < 0; return strcmp(v1->c_str(), v2->c_str()) < 0;
case nPath: case nPath:
// Note: we don't take the accessor into account // Note: we don't take the accessor into account
// since it's not obvious how to compare them in a // since it's not obvious how to compare them in a
// reproducible way. // reproducible way.
return strcmp(v1->_path.path, v2->_path.path) < 0; return strcmp(v1->payload.path.path, v2->payload.path.path) < 0;
case nList: case nList:
// Lexicographic comparison // Lexicographic comparison
for (size_t i = 0;; i++) { for (size_t i = 0;; i++) {
@ -633,13 +633,13 @@ typedef std::list<Value *> ValueList;
#endif #endif
static Bindings::iterator getAttr( static Bindings::const_iterator getAttr(
EvalState & state, EvalState & state,
Symbol attrSym, Symbol attrSym,
Bindings * attrSet, const Bindings * attrSet,
std::string_view errorCtx) std::string_view errorCtx)
{ {
Bindings::iterator value = attrSet->find(attrSym); auto value = attrSet->find(attrSym);
if (value == attrSet->end()) { if (value == attrSet->end()) {
state.error<TypeError>("attribute '%s' missing", state.symbols[attrSym]).withTrace(noPos, errorCtx).debugThrow(); state.error<TypeError>("attribute '%s' missing", state.symbols[attrSym]).withTrace(noPos, errorCtx).debugThrow();
} }
@ -651,7 +651,7 @@ static void prim_genericClosure(EvalState & state, const PosIdx pos, Value * * a
state.forceAttrs(*args[0], noPos, "while evaluating the first argument passed to builtins.genericClosure"); state.forceAttrs(*args[0], noPos, "while evaluating the first argument passed to builtins.genericClosure");
/* Get the start set. */ /* Get the start set. */
Bindings::iterator startSet = getAttr(state, state.sStartSet, args[0]->attrs, "in the attrset passed as argument to builtins.genericClosure"); auto startSet = getAttr(state, state.sStartSet, args[0]->attrs(), "in the attrset passed as argument to builtins.genericClosure");
state.forceList(*startSet->value, noPos, "while evaluating the 'startSet' attribute passed as argument to builtins.genericClosure"); state.forceList(*startSet->value, noPos, "while evaluating the 'startSet' attribute passed as argument to builtins.genericClosure");
@ -665,7 +665,7 @@ static void prim_genericClosure(EvalState & state, const PosIdx pos, Value * * a
} }
/* Get the operator. */ /* Get the operator. */
Bindings::iterator op = getAttr(state, state.sOperator, args[0]->attrs, "in the attrset passed as argument to builtins.genericClosure"); auto op = getAttr(state, state.sOperator, args[0]->attrs(), "in the attrset passed as argument to builtins.genericClosure");
state.forceFunction(*op->value, noPos, "while evaluating the 'operator' attribute passed as argument to builtins.genericClosure"); state.forceFunction(*op->value, noPos, "while evaluating the 'operator' attribute passed as argument to builtins.genericClosure");
/* Construct the closure by applying the operator to elements of /* Construct the closure by applying the operator to elements of
@ -682,7 +682,7 @@ static void prim_genericClosure(EvalState & state, const PosIdx pos, Value * * a
state.forceAttrs(*e, noPos, "while evaluating one of the elements generated by (or initially passed to) builtins.genericClosure"); state.forceAttrs(*e, noPos, "while evaluating one of the elements generated by (or initially passed to) builtins.genericClosure");
Bindings::iterator key = getAttr(state, state.sKey, e->attrs, "in one of the attrsets generated by (or initially passed to) builtins.genericClosure"); auto key = getAttr(state, state.sKey, e->attrs(), "in one of the attrsets generated by (or initially passed to) builtins.genericClosure");
state.forceValue(*key->value, noPos); state.forceValue(*key->value, noPos);
if (!doneKeys.insert(key->value).second) continue; if (!doneKeys.insert(key->value).second) continue;
@ -1050,7 +1050,11 @@ static void prim_second(EvalState & state, const PosIdx pos, Value * * args, Val
* Derivations * Derivations
*************************************************************/ *************************************************************/
static void derivationStrictInternal(EvalState & state, const std::string & name, Bindings * attrs, Value & v); static void derivationStrictInternal(
EvalState & state,
const std::string & name,
const Bindings * attrs,
Value & v);
/* Construct (as a unobservable side effect) a Nix derivation /* Construct (as a unobservable side effect) a Nix derivation
expression that performs the derivation described by the argument expression that performs the derivation described by the argument
@ -1063,10 +1067,10 @@ static void prim_derivationStrict(EvalState & state, const PosIdx pos, Value * *
{ {
state.forceAttrs(*args[0], pos, "while evaluating the argument passed to builtins.derivationStrict"); state.forceAttrs(*args[0], pos, "while evaluating the argument passed to builtins.derivationStrict");
Bindings * attrs = args[0]->attrs; auto attrs = args[0]->attrs();
/* Figure out the name first (for stack backtraces). */ /* Figure out the name first (for stack backtraces). */
Bindings::iterator nameAttr = getAttr(state, state.sName, attrs, "in the attrset passed as argument to builtins.derivationStrict"); auto nameAttr = getAttr(state, state.sName, attrs, "in the attrset passed as argument to builtins.derivationStrict");
std::string drvName; std::string drvName;
try { try {
@ -1105,8 +1109,11 @@ static void prim_derivationStrict(EvalState & state, const PosIdx pos, Value * *
} }
} }
static void derivationStrictInternal(EvalState & state, const std::string & static void derivationStrictInternal(
drvName, Bindings * attrs, Value & v) EvalState & state,
const std::string & drvName,
const Bindings * attrs,
Value & v)
{ {
/* Check whether attributes should be passed as a JSON file. */ /* Check whether attributes should be passed as a JSON file. */
using nlohmann::json; using nlohmann::json;
@ -1708,11 +1715,11 @@ static void prim_findFile(EvalState & state, const PosIdx pos, Value * * args, V
state.forceAttrs(*v2, pos, "while evaluating an element of the list passed to builtins.findFile"); state.forceAttrs(*v2, pos, "while evaluating an element of the list passed to builtins.findFile");
std::string prefix; std::string prefix;
Bindings::iterator i = v2->attrs->find(state.sPrefix); auto i = v2->attrs()->find(state.sPrefix);
if (i != v2->attrs->end()) if (i != v2->attrs()->end())
prefix = state.forceStringNoCtx(*i->value, pos, "while evaluating the `prefix` attribute of an element of the list passed to builtins.findFile"); prefix = state.forceStringNoCtx(*i->value, pos, "while evaluating the `prefix` attribute of an element of the list passed to builtins.findFile");
i = getAttr(state, state.sPath, v2->attrs, "in an element of the __nixPath"); i = getAttr(state, state.sPath, v2->attrs(), "in an element of the __nixPath");
NixStringContext context; NixStringContext context;
auto path = state.coerceToString(pos, *i->value, context, auto path = state.coerceToString(pos, *i->value, context,
@ -2386,7 +2393,7 @@ static void prim_path(EvalState & state, const PosIdx pos, Value * * args, Value
state.forceAttrs(*args[0], pos, "while evaluating the argument passed to 'builtins.path'"); state.forceAttrs(*args[0], pos, "while evaluating the argument passed to 'builtins.path'");
for (auto & attr : *args[0]->attrs) { for (auto & attr : *args[0]->attrs()) {
auto n = state.symbols[attr.name]; auto n = state.symbols[attr.name];
if (n == "path") if (n == "path")
path.emplace(state.coerceToPath(attr.pos, *attr.value, context, "while evaluating the 'path' attribute passed to 'builtins.path'")); path.emplace(state.coerceToPath(attr.pos, *attr.value, context, "while evaluating the 'path' attribute passed to 'builtins.path'"));
@ -2461,9 +2468,9 @@ static void prim_attrNames(EvalState & state, const PosIdx pos, Value * * args,
{ {
state.forceAttrs(*args[0], pos, "while evaluating the argument passed to builtins.attrNames"); state.forceAttrs(*args[0], pos, "while evaluating the argument passed to builtins.attrNames");
auto list = state.buildList(args[0]->attrs->size()); auto list = state.buildList(args[0]->attrs()->size());
for (const auto & [n, i] : enumerate(*args[0]->attrs)) for (const auto & [n, i] : enumerate(*args[0]->attrs()))
(list[n] = state.allocValue())->mkString(state.symbols[i.name]); (list[n] = state.allocValue())->mkString(state.symbols[i.name]);
std::sort(list.begin(), list.end(), std::sort(list.begin(), list.end(),
@ -2489,9 +2496,9 @@ static void prim_attrValues(EvalState & state, const PosIdx pos, Value * * args,
{ {
state.forceAttrs(*args[0], pos, "while evaluating the argument passed to builtins.attrValues"); state.forceAttrs(*args[0], pos, "while evaluating the argument passed to builtins.attrValues");
auto list = state.buildList(args[0]->attrs->size()); auto list = state.buildList(args[0]->attrs()->size());
for (const auto & [n, i] : enumerate(*args[0]->attrs)) for (const auto & [n, i] : enumerate(*args[0]->attrs()))
list[n] = (Value *) &i; list[n] = (Value *) &i;
std::sort(list.begin(), list.end(), std::sort(list.begin(), list.end(),
@ -2522,10 +2529,10 @@ void prim_getAttr(EvalState & state, const PosIdx pos, Value * * args, Value & v
{ {
auto attr = state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.getAttr"); auto attr = state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.getAttr");
state.forceAttrs(*args[1], pos, "while evaluating the second argument passed to builtins.getAttr"); state.forceAttrs(*args[1], pos, "while evaluating the second argument passed to builtins.getAttr");
Bindings::iterator i = getAttr( auto i = getAttr(
state, state,
state.symbols.create(attr), state.symbols.create(attr),
args[1]->attrs, args[1]->attrs(),
"in the attribute set under consideration" "in the attribute set under consideration"
); );
// !!! add to stack trace? // !!! add to stack trace?
@ -2551,8 +2558,8 @@ static void prim_unsafeGetAttrPos(EvalState & state, const PosIdx pos, Value * *
{ {
auto attr = state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.unsafeGetAttrPos"); auto attr = state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.unsafeGetAttrPos");
state.forceAttrs(*args[1], pos, "while evaluating the second argument passed to builtins.unsafeGetAttrPos"); state.forceAttrs(*args[1], pos, "while evaluating the second argument passed to builtins.unsafeGetAttrPos");
Bindings::iterator i = args[1]->attrs->find(state.symbols.create(attr)); auto i = args[1]->attrs()->find(state.symbols.create(attr));
if (i == args[1]->attrs->end()) if (i == args[1]->attrs()->end())
v.mkNull(); v.mkNull();
else else
state.mkPos(v, i->pos); state.mkPos(v, i->pos);
@ -2580,13 +2587,13 @@ static struct LazyPosAcessors {
PrimOp primop_lineOfPos{ PrimOp primop_lineOfPos{
.arity = 1, .arity = 1,
.fun = [] (EvalState & state, PosIdx pos, Value * * args, Value & v) { .fun = [] (EvalState & state, PosIdx pos, Value * * args, Value & v) {
v.mkInt(state.positions[PosIdx(args[0]->integer)].line); v.mkInt(state.positions[PosIdx(args[0]->integer())].line);
} }
}; };
PrimOp primop_columnOfPos{ PrimOp primop_columnOfPos{
.arity = 1, .arity = 1,
.fun = [] (EvalState & state, PosIdx pos, Value * * args, Value & v) { .fun = [] (EvalState & state, PosIdx pos, Value * * args, Value & v) {
v.mkInt(state.positions[PosIdx(args[0]->integer)].column); v.mkInt(state.positions[PosIdx(args[0]->integer())].column);
} }
}; };
@ -2617,7 +2624,7 @@ static void prim_hasAttr(EvalState & state, const PosIdx pos, Value * * args, Va
{ {
auto attr = state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.hasAttr"); auto attr = state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.hasAttr");
state.forceAttrs(*args[1], pos, "while evaluating the second argument passed to builtins.hasAttr"); state.forceAttrs(*args[1], pos, "while evaluating the second argument passed to builtins.hasAttr");
v.mkBool(args[1]->attrs->find(state.symbols.create(attr)) != args[1]->attrs->end()); v.mkBool(args[1]->attrs()->find(state.symbols.create(attr)) != args[1]->attrs()->end());
} }
static RegisterPrimOp primop_hasAttr({ static RegisterPrimOp primop_hasAttr({
@ -2667,9 +2674,9 @@ static void prim_removeAttrs(EvalState & state, const PosIdx pos, Value * * args
/* Copy all attributes not in that set. Note that we don't need /* Copy all attributes not in that set. Note that we don't need
to sort v.attrs because it's a subset of an already sorted to sort v.attrs because it's a subset of an already sorted
vector. */ vector. */
auto attrs = state.buildBindings(args[0]->attrs->size()); auto attrs = state.buildBindings(args[0]->attrs()->size());
std::set_difference( std::set_difference(
args[0]->attrs->begin(), args[0]->attrs->end(), args[0]->attrs()->begin(), args[0]->attrs()->end(),
names.begin(), names.end(), names.begin(), names.end(),
std::back_inserter(attrs)); std::back_inserter(attrs));
v.mkAttrs(attrs.alreadySorted()); v.mkAttrs(attrs.alreadySorted());
@ -2707,13 +2714,13 @@ static void prim_listToAttrs(EvalState & state, const PosIdx pos, Value * * args
for (auto v2 : args[0]->listItems()) { for (auto v2 : args[0]->listItems()) {
state.forceAttrs(*v2, pos, "while evaluating an element of the list passed to builtins.listToAttrs"); state.forceAttrs(*v2, pos, "while evaluating an element of the list passed to builtins.listToAttrs");
Bindings::iterator j = getAttr(state, state.sName, v2->attrs, "in a {name=...; value=...;} pair"); auto j = getAttr(state, state.sName, v2->attrs(), "in a {name=...; value=...;} pair");
auto name = state.forceStringNoCtx(*j->value, j->pos, "while evaluating the `name` attribute of an element of the list passed to builtins.listToAttrs"); auto name = state.forceStringNoCtx(*j->value, j->pos, "while evaluating the `name` attribute of an element of the list passed to builtins.listToAttrs");
auto sym = state.symbols.create(name); auto sym = state.symbols.create(name);
if (seen.insert(sym).second) { if (seen.insert(sym).second) {
Bindings::iterator j2 = getAttr(state, state.sValue, v2->attrs, "in a {name=...; value=...;} pair"); auto j2 = getAttr(state, state.sValue, v2->attrs(), "in a {name=...; value=...;} pair");
attrs.insert(sym, j2->value, j2->pos); attrs.insert(sym, j2->value, j2->pos);
} }
} }
@ -2757,8 +2764,8 @@ static void prim_intersectAttrs(EvalState & state, const PosIdx pos, Value * * a
state.forceAttrs(*args[0], pos, "while evaluating the first argument passed to builtins.intersectAttrs"); state.forceAttrs(*args[0], pos, "while evaluating the first argument passed to builtins.intersectAttrs");
state.forceAttrs(*args[1], pos, "while evaluating the second argument passed to builtins.intersectAttrs"); state.forceAttrs(*args[1], pos, "while evaluating the second argument passed to builtins.intersectAttrs");
Bindings &left = *args[0]->attrs; auto & left = *args[0]->attrs();
Bindings &right = *args[1]->attrs; auto & right = *args[1]->attrs();
auto attrs = state.buildBindings(std::min(left.size(), right.size())); auto attrs = state.buildBindings(std::min(left.size(), right.size()));
@ -2802,14 +2809,14 @@ static void prim_intersectAttrs(EvalState & state, const PosIdx pos, Value * * a
if (left.size() < right.size()) { if (left.size() < right.size()) {
for (auto & l : left) { for (auto & l : left) {
Bindings::iterator r = right.find(l.name); auto r = right.find(l.name);
if (r != right.end()) if (r != right.end())
attrs.insert(*r); attrs.insert(*r);
} }
} }
else { else {
for (auto & r : right) { for (auto & r : right) {
Bindings::iterator l = left.find(r.name); auto l = left.find(r.name);
if (l != left.end()) if (l != left.end())
attrs.insert(r); attrs.insert(r);
} }
@ -2840,8 +2847,7 @@ static void prim_catAttrs(EvalState & state, const PosIdx pos, Value * * args, V
for (auto v2 : args[1]->listItems()) { for (auto v2 : args[1]->listItems()) {
state.forceAttrs(*v2, pos, "while evaluating an element in the list passed as second argument to builtins.catAttrs"); state.forceAttrs(*v2, pos, "while evaluating an element in the list passed as second argument to builtins.catAttrs");
Bindings::iterator i = v2->attrs->find(attrName); if (auto i = v2->attrs()->get(attrName))
if (i != v2->attrs->end())
res[found++] = i->value; res[found++] = i->value;
} }
@ -2878,13 +2884,13 @@ static void prim_functionArgs(EvalState & state, const PosIdx pos, Value * * arg
if (!args[0]->isLambda()) if (!args[0]->isLambda())
state.error<TypeError>("'functionArgs' requires a function").atPos(pos).debugThrow(); state.error<TypeError>("'functionArgs' requires a function").atPos(pos).debugThrow();
if (!args[0]->lambda.fun->hasFormals()) { if (!args[0]->payload.lambda.fun->hasFormals()) {
v.mkAttrs(&state.emptyBindings); v.mkAttrs(&state.emptyBindings);
return; return;
} }
auto attrs = state.buildBindings(args[0]->lambda.fun->formals->formals.size()); auto attrs = state.buildBindings(args[0]->payload.lambda.fun->formals->formals.size());
for (auto & i : args[0]->lambda.fun->formals->formals) for (auto & i : args[0]->payload.lambda.fun->formals->formals)
attrs.insert(i.name, state.getBool(i.def), i.pos); attrs.insert(i.name, state.getBool(i.def), i.pos);
v.mkAttrs(attrs); v.mkAttrs(attrs);
} }
@ -2911,9 +2917,9 @@ static void prim_mapAttrs(EvalState & state, const PosIdx pos, Value * * args, V
{ {
state.forceAttrs(*args[1], pos, "while evaluating the second argument passed to builtins.mapAttrs"); state.forceAttrs(*args[1], pos, "while evaluating the second argument passed to builtins.mapAttrs");
auto attrs = state.buildBindings(args[1]->attrs->size()); auto attrs = state.buildBindings(args[1]->attrs()->size());
for (auto & i : *args[1]->attrs) { for (auto & i : *args[1]->attrs()) {
Value * vName = state.allocValue(); Value * vName = state.allocValue();
Value * vFun2 = state.allocValue(); Value * vFun2 = state.allocValue();
vName->mkString(state.symbols[i.name]); vName->mkString(state.symbols[i.name]);
@ -2963,7 +2969,7 @@ static void prim_zipAttrsWith(EvalState & state, const PosIdx pos, Value * * arg
for (auto & vElem : listItems) { for (auto & vElem : listItems) {
state.forceAttrs(*vElem, noPos, "while evaluating a value of the list passed as second argument to builtins.zipAttrsWith"); state.forceAttrs(*vElem, noPos, "while evaluating a value of the list passed as second argument to builtins.zipAttrsWith");
for (auto & attr : *vElem->attrs) for (auto & attr : *vElem->attrs())
attrsSeen.try_emplace(attr.name).first->second.size++; attrsSeen.try_emplace(attr.name).first->second.size++;
} }
@ -2971,7 +2977,7 @@ static void prim_zipAttrsWith(EvalState & state, const PosIdx pos, Value * * arg
elem.list.emplace(state.buildList(elem.size)); elem.list.emplace(state.buildList(elem.size));
for (auto & vElem : listItems) { for (auto & vElem : listItems) {
for (auto & attr : *vElem->attrs) { for (auto & attr : *vElem->attrs()) {
auto & item = attrsSeen.at(attr.name); auto & item = attrsSeen.at(attr.name);
(*item.list)[item.pos++] = attr.value; (*item.list)[item.pos++] = attr.value;
} }
@ -3413,10 +3419,8 @@ static void prim_sort(EvalState & state, const PosIdx pos, Value * * args, Value
auto comparator = [&](Value * a, Value * b) { auto comparator = [&](Value * a, Value * b) {
/* Optimization: if the comparator is lessThan, bypass /* Optimization: if the comparator is lessThan, bypass
callFunction. */ callFunction. */
/* TODO: (layus) this is absurd. An optimisation like this
should be outside the lambda creation */
if (args[0]->isPrimOp()) { if (args[0]->isPrimOp()) {
auto ptr = args[0]->primOp->fun.target<decltype(&prim_lessThan)>(); auto ptr = args[0]->primOp()->fun.target<decltype(&prim_lessThan)>();
if (ptr && *ptr == prim_lessThan) if (ptr && *ptr == prim_lessThan)
return CompareValues(state, noPos, "while evaluating the ordering function passed to builtins.sort")(a, b); return CompareValues(state, noPos, "while evaluating the ordering function passed to builtins.sort")(a, b);
} }
@ -3909,18 +3913,17 @@ static RegisterPrimOp primop_hashString({
static void prim_convertHash(EvalState & state, const PosIdx pos, Value * * args, Value & v) static void prim_convertHash(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {
state.forceAttrs(*args[0], pos, "while evaluating the first argument passed to builtins.convertHash"); state.forceAttrs(*args[0], pos, "while evaluating the first argument passed to builtins.convertHash");
auto &inputAttrs = args[0]->attrs; auto inputAttrs = args[0]->attrs();
Bindings::iterator iteratorHash = getAttr(state, state.symbols.create("hash"), inputAttrs, "while locating the attribute 'hash'"); auto iteratorHash = getAttr(state, state.symbols.create("hash"), inputAttrs, "while locating the attribute 'hash'");
auto hash = state.forceStringNoCtx(*iteratorHash->value, pos, "while evaluating the attribute 'hash'"); auto hash = state.forceStringNoCtx(*iteratorHash->value, pos, "while evaluating the attribute 'hash'");
Bindings::iterator iteratorHashAlgo = inputAttrs->find(state.symbols.create("hashAlgo")); auto iteratorHashAlgo = inputAttrs->get(state.symbols.create("hashAlgo"));
std::optional<HashAlgorithm> ha = std::nullopt; std::optional<HashAlgorithm> ha = std::nullopt;
if (iteratorHashAlgo != inputAttrs->end()) { if (iteratorHashAlgo)
ha = parseHashAlgo(state.forceStringNoCtx(*iteratorHashAlgo->value, pos, "while evaluating the attribute 'hashAlgo'")); ha = parseHashAlgo(state.forceStringNoCtx(*iteratorHashAlgo->value, pos, "while evaluating the attribute 'hashAlgo'"));
}
Bindings::iterator iteratorToHashFormat = getAttr(state, state.symbols.create("toHashFormat"), args[0]->attrs, "while locating the attribute 'toHashFormat'"); auto iteratorToHashFormat = getAttr(state, state.symbols.create("toHashFormat"), args[0]->attrs(), "while locating the attribute 'toHashFormat'");
HashFormat hf = parseHashFormat(state.forceStringNoCtx(*iteratorToHashFormat->value, pos, "while evaluating the attribute 'toHashFormat'")); HashFormat hf = parseHashFormat(state.forceStringNoCtx(*iteratorToHashFormat->value, pos, "while evaluating the attribute 'toHashFormat'"));
v.mkString(Hash::parseAny(hash, ha).to_string(hf, hf == HashFormat::SRI)); v.mkString(Hash::parseAny(hash, ha).to_string(hf, hf == HashFormat::SRI));
@ -4666,7 +4669,7 @@ void EvalState::createBaseEnv()
/* Now that we've added all primops, sort the `builtins' set, /* Now that we've added all primops, sort the `builtins' set,
because attribute lookups expect it to be sorted. */ because attribute lookups expect it to be sorted. */
baseEnv.values[0]->attrs->sort(); baseEnv.values[0]->payload.attrs->sort();
staticBaseEnv->sort(); staticBaseEnv->sort();

View file

@ -258,7 +258,7 @@ static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * ar
auto sPath = state.symbols.create("path"); auto sPath = state.symbols.create("path");
auto sAllOutputs = state.symbols.create("allOutputs"); auto sAllOutputs = state.symbols.create("allOutputs");
for (auto & i : *args[1]->attrs) { for (auto & i : *args[1]->attrs()) {
const auto & name = state.symbols[i.name]; const auto & name = state.symbols[i.name];
if (!state.store->isStorePath(name)) if (!state.store->isStorePath(name))
state.error<EvalError>( state.error<EvalError>(
@ -269,17 +269,16 @@ static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * ar
if (!settings.readOnlyMode) if (!settings.readOnlyMode)
state.store->ensurePath(namePath); state.store->ensurePath(namePath);
state.forceAttrs(*i.value, i.pos, "while evaluating the value of a string context"); state.forceAttrs(*i.value, i.pos, "while evaluating the value of a string context");
auto iter = i.value->attrs->find(sPath);
if (iter != i.value->attrs->end()) { if (auto attr = i.value->attrs()->get(sPath)) {
if (state.forceBool(*iter->value, iter->pos, "while evaluating the `path` attribute of a string context")) if (state.forceBool(*attr->value, attr->pos, "while evaluating the `path` attribute of a string context"))
context.emplace(NixStringContextElem::Opaque { context.emplace(NixStringContextElem::Opaque {
.path = namePath, .path = namePath,
}); });
} }
iter = i.value->attrs->find(sAllOutputs); if (auto attr = i.value->attrs()->get(sAllOutputs)) {
if (iter != i.value->attrs->end()) { if (state.forceBool(*attr->value, attr->pos, "while evaluating the `allOutputs` attribute of a string context")) {
if (state.forceBool(*iter->value, iter->pos, "while evaluating the `allOutputs` attribute of a string context")) {
if (!isDerivation(name)) { if (!isDerivation(name)) {
state.error<EvalError>( state.error<EvalError>(
"tried to add all-outputs context of %s, which is not a derivation, to a string", "tried to add all-outputs context of %s, which is not a derivation, to a string",
@ -292,17 +291,16 @@ static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * ar
} }
} }
iter = i.value->attrs->find(state.sOutputs); if (auto attr = i.value->attrs()->get(state.sOutputs)) {
if (iter != i.value->attrs->end()) { state.forceList(*attr->value, attr->pos, "while evaluating the `outputs` attribute of a string context");
state.forceList(*iter->value, iter->pos, "while evaluating the `outputs` attribute of a string context"); if (attr->value->listSize() && !isDerivation(name)) {
if (iter->value->listSize() && !isDerivation(name)) {
state.error<EvalError>( state.error<EvalError>(
"tried to add derivation output context of %s, which is not a derivation, to a string", "tried to add derivation output context of %s, which is not a derivation, to a string",
name name
).atPos(i.pos).debugThrow(); ).atPos(i.pos).debugThrow();
} }
for (auto elem : iter->value->listItems()) { for (auto elem : attr->value->listItems()) {
auto outputName = state.forceStringNoCtx(*elem, iter->pos, "while evaluating an output name within a string context"); auto outputName = state.forceStringNoCtx(*elem, attr->pos, "while evaluating an output name within a string context");
context.emplace(NixStringContextElem::Built { context.emplace(NixStringContextElem::Built {
.drvPath = makeConstantStorePathRef(namePath), .drvPath = makeConstantStorePathRef(namePath),
.output = std::string { outputName }, .output = std::string { outputName },

View file

@ -121,7 +121,7 @@ static void prim_fetchClosure(EvalState & state, const PosIdx pos, Value * * arg
std::optional<StorePathOrGap> toPath; std::optional<StorePathOrGap> toPath;
std::optional<bool> inputAddressedMaybe; std::optional<bool> inputAddressedMaybe;
for (auto & attr : *args[0]->attrs) { for (auto & attr : *args[0]->attrs()) {
const auto & attrName = state.symbols[attr.name]; const auto & attrName = state.symbols[attr.name];
auto attrHint = [&]() -> std::string { auto attrHint = [&]() -> std::string {
return "while evaluating the '" + attrName + "' attribute passed to builtins.fetchClosure"; return "while evaluating the '" + attrName + "' attribute passed to builtins.fetchClosure";

View file

@ -20,7 +20,7 @@ static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value * * a
if (args[0]->type() == nAttrs) { if (args[0]->type() == nAttrs) {
for (auto & attr : *args[0]->attrs) { for (auto & attr : *args[0]->attrs()) {
std::string_view n(state.symbols[attr.name]); std::string_view n(state.symbols[attr.name]);
if (n == "url") if (n == "url")
url = state.coerceToString(attr.pos, *attr.value, context, url = state.coerceToString(attr.pos, *attr.value, context,

View file

@ -97,7 +97,7 @@ static void fetchTree(
fetchers::Attrs attrs; fetchers::Attrs attrs;
if (auto aType = args[0]->attrs->get(state.sType)) { if (auto aType = args[0]->attrs()->get(state.sType)) {
if (type) if (type)
state.error<EvalError>( state.error<EvalError>(
"unexpected attribute 'type'" "unexpected attribute 'type'"
@ -110,7 +110,7 @@ static void fetchTree(
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) {
@ -121,9 +121,9 @@ static void fetchTree(
: s); : s);
} }
else if (attr.value->type() == nBool) else if (attr.value->type() == nBool)
attrs.emplace(state.symbols[attr.name], Explicit<bool>{attr.value->boolean}); attrs.emplace(state.symbols[attr.name], Explicit<bool>{attr.value->boolean()});
else if (attr.value->type() == nInt) else if (attr.value->type() == nInt)
attrs.emplace(state.symbols[attr.name], uint64_t(attr.value->integer)); attrs.emplace(state.symbols[attr.name], uint64_t(attr.value->integer()));
else if (state.symbols[attr.name] == "publicKeys") { else if (state.symbols[attr.name] == "publicKeys") {
experimentalFeatureSettings.require(Xp::VerifiedFetches); experimentalFeatureSettings.require(Xp::VerifiedFetches);
attrs.emplace(state.symbols[attr.name], printValueAsJSON(state, true, *attr.value, pos, context).dump()); attrs.emplace(state.symbols[attr.name], printValueAsJSON(state, true, *attr.value, pos, context).dump());
@ -422,7 +422,7 @@ static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v
if (args[0]->type() == nAttrs) { if (args[0]->type() == nAttrs) {
for (auto & attr : *args[0]->attrs) { for (auto & attr : *args[0]->attrs()) {
std::string_view n(state.symbols[attr.name]); std::string_view n(state.symbols[attr.name]);
if (n == "url") if (n == "url")
url = state.forceStringNoCtx(*attr.value, attr.pos, "while evaluating the url we should fetch"); url = state.forceStringNoCtx(*attr.value, attr.pos, "while evaluating the url we should fetch");

View file

@ -21,10 +21,10 @@ void printAmbiguous(
} }
switch (v.type()) { switch (v.type()) {
case nInt: case nInt:
str << v.integer; str << v.integer();
break; break;
case nBool: case nBool:
printLiteralBool(str, v.boolean); printLiteralBool(str, v.boolean());
break; break;
case nString: case nString:
printLiteralString(str, v.string_view()); printLiteralString(str, v.string_view());
@ -36,11 +36,11 @@ void printAmbiguous(
str << "null"; str << "null";
break; break;
case nAttrs: { case nAttrs: {
if (seen && !v.attrs->empty() && !seen->insert(v.attrs).second) if (seen && !v.attrs()->empty() && !seen->insert(v.attrs()).second)
str << "«repeated»"; str << "«repeated»";
else { else {
str << "{ "; str << "{ ";
for (auto & i : v.attrs->lexicographicOrder(symbols)) { for (auto & i : v.attrs()->lexicographicOrder(symbols)) {
str << symbols[i->name] << " = "; str << symbols[i->name] << " = ";
printAmbiguous(*i->value, symbols, str, seen, depth - 1); printAmbiguous(*i->value, symbols, str, seen, depth - 1);
str << "; "; str << "; ";
@ -87,10 +87,10 @@ void printAmbiguous(
} }
break; break;
case nExternal: case nExternal:
str << *v.external; str << *v.external();
break; break;
case nFloat: case nFloat:
str << v.fpoint; str << v.fpoint();
break; break;
default: default:
printError("Nix evaluator internal error: printAmbiguous: invalid value type"); printError("Nix evaluator internal error: printAmbiguous: invalid value type");

View file

@ -223,7 +223,7 @@ private:
{ {
if (options.ansiColors) if (options.ansiColors)
output << ANSI_CYAN; output << ANSI_CYAN;
output << v.integer; output << v.integer();
if (options.ansiColors) if (options.ansiColors)
output << ANSI_NORMAL; output << ANSI_NORMAL;
} }
@ -232,7 +232,7 @@ private:
{ {
if (options.ansiColors) if (options.ansiColors)
output << ANSI_CYAN; output << ANSI_CYAN;
output << v.fpoint; output << v.fpoint();
if (options.ansiColors) if (options.ansiColors)
output << ANSI_NORMAL; output << ANSI_NORMAL;
} }
@ -241,7 +241,7 @@ private:
{ {
if (options.ansiColors) if (options.ansiColors)
output << ANSI_CYAN; output << ANSI_CYAN;
printLiteralBool(output, v.boolean); printLiteralBool(output, v.boolean());
if (options.ansiColors) if (options.ansiColors)
output << ANSI_NORMAL; output << ANSI_NORMAL;
} }
@ -271,10 +271,9 @@ private:
void printDerivation(Value & v) void printDerivation(Value & v)
{ {
Bindings::iterator i = v.attrs->find(state.sDrvPath);
NixStringContext context; NixStringContext context;
std::string storePath; std::string storePath;
if (i != v.attrs->end()) if (auto i = v.attrs()->get(state.sDrvPath))
storePath = state.store->printStorePath(state.coerceToStorePath(i->pos, *i->value, context, "while evaluating the drvPath of a derivation")); storePath = state.store->printStorePath(state.coerceToStorePath(i->pos, *i->value, context, "while evaluating the drvPath of a derivation"));
if (options.ansiColors) if (options.ansiColors)
@ -312,7 +311,7 @@ private:
void printAttrs(Value & v, size_t depth) void printAttrs(Value & v, size_t depth)
{ {
if (seen && !seen->insert(v.attrs).second) { if (seen && !seen->insert(v.attrs()).second) {
printRepeated(); printRepeated();
return; return;
} }
@ -324,7 +323,7 @@ private:
output << "{"; output << "{";
AttrVec sorted; AttrVec sorted;
for (auto & i : *v.attrs) for (auto & i : *v.attrs())
sorted.emplace_back(std::pair(state.symbols[i.name], i.value)); sorted.emplace_back(std::pair(state.symbols[i.name], i.value));
if (options.maxAttrs == std::numeric_limits<size_t>::max()) if (options.maxAttrs == std::numeric_limits<size_t>::max())
@ -423,18 +422,18 @@ private:
if (v.isLambda()) { if (v.isLambda()) {
output << "lambda"; output << "lambda";
if (v.lambda.fun) { if (v.payload.lambda.fun) {
if (v.lambda.fun->name) { if (v.payload.lambda.fun->name) {
output << " " << state.symbols[v.lambda.fun->name]; output << " " << state.symbols[v.payload.lambda.fun->name];
} }
std::ostringstream s; std::ostringstream s;
s << state.positions[v.lambda.fun->pos]; s << state.positions[v.payload.lambda.fun->pos];
output << " @ " << filterANSIEscapes(s.str()); output << " @ " << filterANSIEscapes(s.str());
} }
} else if (v.isPrimOp()) { } else if (v.isPrimOp()) {
if (v.primOp) if (v.primOp())
output << *v.primOp; output << *v.primOp();
else else
output << "primop"; output << "primop";
} else if (v.isPrimOpApp()) { } else if (v.isPrimOpApp()) {
@ -480,7 +479,7 @@ private:
void printExternal(Value & v) void printExternal(Value & v)
{ {
v.external->print(output); v.external()->print(output);
} }
void printUnknown() void printUnknown()

View file

@ -22,11 +22,11 @@ json printValueAsJSON(EvalState & state, bool strict,
switch (v.type()) { switch (v.type()) {
case nInt: case nInt:
out = v.integer; out = v.integer();
break; break;
case nBool: case nBool:
out = v.boolean; out = v.boolean();
break; break;
case nString: case nString:
@ -52,24 +52,20 @@ json printValueAsJSON(EvalState & state, bool strict,
out = *maybeString; out = *maybeString;
break; break;
} }
auto i = v.attrs->find(state.sOutPath); if (auto i = v.attrs()->get(state.sOutPath))
if (i == v.attrs->end()) { return printValueAsJSON(state, strict, *i->value, i->pos, context, copyToStore);
else {
out = json::object(); out = json::object();
StringSet names; for (auto & a : v.attrs()->lexicographicOrder(state.symbols)) {
for (auto & j : *v.attrs)
names.emplace(state.symbols[j.name]);
for (auto & j : names) {
Attr & a(*v.attrs->find(state.symbols.create(j)));
try { try {
out[j] = printValueAsJSON(state, strict, *a.value, a.pos, context, copyToStore); out[state.symbols[a->name]] = printValueAsJSON(state, strict, *a->value, a->pos, context, copyToStore);
} catch (Error & e) { } catch (Error & e) {
e.addTrace(state.positions[a.pos], e.addTrace(state.positions[a->pos],
HintFmt("while evaluating attribute '%1%'", j)); HintFmt("while evaluating attribute '%1%'", state.symbols[a->name]));
throw; throw;
} }
} }
} else }
return printValueAsJSON(state, strict, *i->value, i->pos, context, copyToStore);
break; break;
} }
@ -90,11 +86,11 @@ json printValueAsJSON(EvalState & state, bool strict,
} }
case nExternal: case nExternal:
return v.external->printValueAsJSON(state, strict, context, copyToStore); return v.external()->printValueAsJSON(state, strict, context, copyToStore);
break; break;
case nFloat: case nFloat:
out = v.fpoint; out = v.fpoint();
break; break;
case nThunk: case nThunk:

View file

@ -32,23 +32,18 @@ static void posToXML(EvalState & state, XMLAttrs & xmlAttrs, const Pos & pos)
static void showAttrs(EvalState & state, bool strict, bool location, static void showAttrs(EvalState & state, bool strict, bool location,
Bindings & attrs, XMLWriter & doc, NixStringContext & context, PathSet & drvsSeen) const Bindings & attrs, XMLWriter & doc, NixStringContext & context, PathSet & drvsSeen)
{ {
StringSet names; StringSet names;
for (auto & i : attrs) for (auto & a : attrs.lexicographicOrder(state.symbols)) {
names.emplace(state.symbols[i.name]);
for (auto & i : names) {
Attr & a(*attrs.find(state.symbols.create(i)));
XMLAttrs xmlAttrs; XMLAttrs xmlAttrs;
xmlAttrs["name"] = i; xmlAttrs["name"] = state.symbols[a->name];
if (location && a.pos) posToXML(state, xmlAttrs, state.positions[a.pos]); if (location && a->pos) posToXML(state, 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);
} }
} }
@ -64,11 +59,11 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
switch (v.type()) { switch (v.type()) {
case nInt: case nInt:
doc.writeEmptyElement("int", singletonAttrs("value", fmt("%1%", v.integer))); doc.writeEmptyElement("int", singletonAttrs("value", fmt("%1%", v.integer())));
break; break;
case nBool: case nBool:
doc.writeEmptyElement("bool", singletonAttrs("value", v.boolean ? "true" : "false")); doc.writeEmptyElement("bool", singletonAttrs("value", v.boolean() ? "true" : "false"));
break; break;
case nString: case nString:
@ -89,18 +84,14 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
if (state.isDerivation(v)) { if (state.isDerivation(v)) {
XMLAttrs xmlAttrs; XMLAttrs xmlAttrs;
Bindings::iterator a = v.attrs->find(state.symbols.create("derivation"));
Path drvPath; Path drvPath;
a = v.attrs->find(state.sDrvPath); if (auto a = v.attrs()->get(state.sDrvPath)) {
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->c_str(); xmlAttrs["drvPath"] = drvPath = a->value->c_str();
} }
a = v.attrs->find(state.sOutPath); if (auto a = v.attrs()->get(state.sOutPath)) {
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->c_str(); xmlAttrs["outPath"] = a->value->c_str();
@ -109,14 +100,14 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
XMLOpenElement _(doc, "derivation", xmlAttrs); XMLOpenElement _(doc, "derivation", xmlAttrs);
if (drvPath != "" && drvsSeen.insert(drvPath).second) if (drvPath != "" && drvsSeen.insert(drvPath).second)
showAttrs(state, strict, location, *v.attrs, doc, context, drvsSeen); showAttrs(state, strict, location, *v.attrs(), doc, context, drvsSeen);
else else
doc.writeEmptyElement("repeated"); doc.writeEmptyElement("repeated");
} }
else { else {
XMLOpenElement _(doc, "attrs"); XMLOpenElement _(doc, "attrs");
showAttrs(state, strict, location, *v.attrs, doc, context, drvsSeen); showAttrs(state, strict, location, *v.attrs(), doc, context, drvsSeen);
} }
break; break;
@ -135,28 +126,28 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
break; break;
} }
XMLAttrs xmlAttrs; XMLAttrs xmlAttrs;
if (location) posToXML(state, xmlAttrs, state.positions[v.lambda.fun->pos]); if (location) posToXML(state, xmlAttrs, state.positions[v.payload.lambda.fun->pos]);
XMLOpenElement _(doc, "function", xmlAttrs); XMLOpenElement _(doc, "function", xmlAttrs);
if (v.lambda.fun->hasFormals()) { if (v.payload.lambda.fun->hasFormals()) {
XMLAttrs attrs; XMLAttrs attrs;
if (v.lambda.fun->arg) attrs["name"] = state.symbols[v.lambda.fun->arg]; if (v.payload.lambda.fun->arg) attrs["name"] = state.symbols[v.payload.lambda.fun->arg];
if (v.lambda.fun->formals->ellipsis) attrs["ellipsis"] = "1"; if (v.payload.lambda.fun->formals->ellipsis) attrs["ellipsis"] = "1";
XMLOpenElement _(doc, "attrspat", attrs); XMLOpenElement _(doc, "attrspat", attrs);
for (auto & i : v.lambda.fun->formals->lexicographicOrder(state.symbols)) for (auto & i : v.payload.lambda.fun->formals->lexicographicOrder(state.symbols))
doc.writeEmptyElement("attr", singletonAttrs("name", state.symbols[i.name])); doc.writeEmptyElement("attr", singletonAttrs("name", state.symbols[i.name]));
} else } else
doc.writeEmptyElement("varpat", singletonAttrs("name", state.symbols[v.lambda.fun->arg])); doc.writeEmptyElement("varpat", singletonAttrs("name", state.symbols[v.payload.lambda.fun->arg]));
break; break;
} }
case nExternal: case nExternal:
v.external->printValueAsXML(state, strict, location, doc, context, drvsSeen, pos); v.external()->printValueAsXML(state, strict, location, doc, context, drvsSeen, pos);
break; break;
case nFloat: case nFloat:
doc.writeEmptyElement("float", singletonAttrs("value", fmt("%1%", v.fpoint))); doc.writeEmptyElement("float", singletonAttrs("value", fmt("%1%", v.fpoint())));
break; break;
case nThunk: case nThunk:

View file

@ -234,14 +234,14 @@ public:
ExprLambda * fun; ExprLambda * fun;
}; };
union using Payload = union
{ {
NixInt integer; NixInt integer;
bool boolean; bool boolean;
StringWithContext string; StringWithContext string;
Path _path; Path path;
Bindings * attrs; Bindings * attrs;
struct { struct {
@ -258,6 +258,8 @@ public:
NixFloat fpoint; NixFloat fpoint;
}; };
Payload payload;
/** /**
* Returns the normal type of a Value. This only returns nThunk if * Returns the normal type of a Value. This only returns nThunk if
* the Value hasn't been forceValue'd * the Value hasn't been forceValue'd
@ -286,34 +288,25 @@ public:
abort(); abort();
} }
/** inline void finishValue(InternalType newType, Payload newPayload)
* After overwriting an app node, be sure to clear pointers in the
* Value to ensure that the target isn't kept alive unnecessarily.
*/
inline void clearValue()
{ {
app.left = app.right = 0; payload = newPayload;
internalType = newType;
} }
inline void mkInt(NixInt n) inline void mkInt(NixInt n)
{ {
clearValue(); finishValue(tInt, { .integer = n });
internalType = tInt;
integer = n;
} }
inline void mkBool(bool b) inline void mkBool(bool b)
{ {
clearValue(); finishValue(tBool, { .boolean = b });
internalType = tBool;
boolean = b;
} }
inline void mkString(const char * s, const char * * context = 0) inline void mkString(const char * s, const char * * context = 0)
{ {
internalType = tString; finishValue(tString, { .string = { .c_str = s, .context = context } });
string.c_str = s;
string.context = context;
} }
void mkString(std::string_view s); void mkString(std::string_view s);
@ -332,63 +325,44 @@ public:
inline void mkPath(InputAccessor * accessor, const char * path) inline void mkPath(InputAccessor * accessor, const char * path)
{ {
clearValue(); finishValue(tPath, { .path = { .accessor = accessor, .path = path } });
internalType = tPath;
_path.accessor = accessor;
_path.path = path;
} }
inline void mkNull() inline void mkNull()
{ {
clearValue(); finishValue(tNull, {});
internalType = tNull;
} }
inline void mkAttrs(Bindings * a) inline void mkAttrs(Bindings * a)
{ {
clearValue(); finishValue(tAttrs, { .attrs = a });
internalType = tAttrs;
attrs = a;
} }
Value & mkAttrs(BindingsBuilder & bindings); Value & mkAttrs(BindingsBuilder & bindings);
void mkList(const ListBuilder & builder) void mkList(const ListBuilder & builder)
{ {
clearValue(); if (builder.size == 1)
if (builder.size == 1) { finishValue(tList1, { .smallList = { builder.inlineElems[0] } });
smallList[0] = builder.inlineElems[0]; else if (builder.size == 2)
internalType = tList1; finishValue(tList2, { .smallList = { builder.inlineElems[0], builder.inlineElems[1] } });
} else if (builder.size == 2) { else
smallList[0] = builder.inlineElems[0]; finishValue(tListN, { .bigList = { .size = builder.size, .elems = builder.elems } });
smallList[1] = builder.inlineElems[1];
internalType = tList2;
} else {
bigList.size = builder.size;
bigList.elems = builder.elems;
internalType = tListN;
}
} }
inline void mkThunk(Env * e, Expr * ex) inline void mkThunk(Env * e, Expr * ex)
{ {
internalType = tThunk; finishValue(tThunk, { .thunk = { .env = e, .expr = ex } });
thunk.env = e;
thunk.expr = ex;
} }
inline void mkApp(Value * l, Value * r) inline void mkApp(Value * l, Value * r)
{ {
internalType = tApp; finishValue(tApp, { .app = { .left = l, .right = r } });
app.left = l;
app.right = r;
} }
inline void mkLambda(Env * e, ExprLambda * f) inline void mkLambda(Env * e, ExprLambda * f)
{ {
internalType = tLambda; finishValue(tLambda, { .lambda = { .env = e, .fun = f } });
lambda.env = e;
lambda.fun = f;
} }
inline void mkBlackhole(); inline void mkBlackhole();
@ -397,28 +371,22 @@ public:
inline void mkPrimOpApp(Value * l, Value * r) inline void mkPrimOpApp(Value * l, Value * r)
{ {
internalType = tPrimOpApp; finishValue(tPrimOpApp, { .primOpApp = { .left = l, .right = r } });
primOpApp.left = l;
primOpApp.right = r;
} }
/** /**
* For a `tPrimOpApp` value, get the original `PrimOp` value. * For a `tPrimOpApp` value, get the original `PrimOp` value.
*/ */
PrimOp * primOpAppPrimOp() const; const PrimOp * primOpAppPrimOp() const;
inline void mkExternal(ExternalValueBase * e) inline void mkExternal(ExternalValueBase * e)
{ {
clearValue(); finishValue(tExternal, { .external = e });
internalType = tExternal;
external = e;
} }
inline void mkFloat(NixFloat n) inline void mkFloat(NixFloat n)
{ {
clearValue(); finishValue(tFloat, { .fpoint = n });
internalType = tFloat;
fpoint = n;
} }
bool isList() const bool isList() const
@ -428,7 +396,7 @@ public:
Value * const * listElems() Value * const * listElems()
{ {
return internalType == tList1 || internalType == tList2 ? smallList : bigList.elems; return internalType == tList1 || internalType == tList2 ? payload.smallList : payload.bigList.elems;
} }
std::span<Value * const> listItems() const std::span<Value * const> listItems() const
@ -439,12 +407,12 @@ public:
Value * const * listElems() const Value * const * listElems() const
{ {
return internalType == tList1 || internalType == tList2 ? smallList : bigList.elems; return internalType == tList1 || internalType == tList2 ? payload.smallList : payload.bigList.elems;
} }
size_t listSize() const size_t listSize() const
{ {
return internalType == tList1 ? 1 : internalType == tList2 ? 2 : bigList.size; return internalType == tList1 ? 1 : internalType == tList2 ? 2 : payload.bigList.size;
} }
PosIdx determinePos(const PosIdx pos) const; PosIdx determinePos(const PosIdx pos) const;
@ -460,26 +428,44 @@ public:
{ {
assert(internalType == tPath); assert(internalType == tPath);
return SourcePath( return SourcePath(
ref(_path.accessor->shared_from_this()), ref(payload.path.accessor->shared_from_this()),
CanonPath(CanonPath::unchecked_t(), _path.path)); CanonPath(CanonPath::unchecked_t(), payload.path.path));
} }
std::string_view string_view() const std::string_view string_view() const
{ {
assert(internalType == tString); assert(internalType == tString);
return std::string_view(string.c_str); return std::string_view(payload.string.c_str);
} }
const char * const c_str() const const char * const c_str() const
{ {
assert(internalType == tString); assert(internalType == tString);
return string.c_str; return payload.string.c_str;
} }
const char * * context() const const char * * context() const
{ {
return string.context; return payload.string.context;
} }
ExternalValueBase * external() const
{ return payload.external; }
const Bindings * attrs() const
{ return payload.attrs; }
const PrimOp * primOp() const
{ return payload.primOp; }
bool boolean() const
{ return payload.boolean; }
NixInt integer() const
{ return payload.integer; }
NixFloat fpoint() const
{ return payload.fpoint; }
}; };
@ -487,13 +473,12 @@ extern ExprBlackHole eBlackHole;
bool Value::isBlackhole() const bool Value::isBlackhole() const
{ {
return internalType == tThunk && thunk.expr == (Expr*) &eBlackHole; return internalType == tThunk && payload.thunk.expr == (Expr*) &eBlackHole;
} }
void Value::mkBlackhole() void Value::mkBlackhole()
{ {
internalType = tThunk; mkThunk(nullptr, (Expr *) &eBlackHole);
thunk.expr = (Expr*) &eBlackHole;
} }

View file

@ -333,8 +333,8 @@ static void main_nix_build(int argc, char * * argv)
return false; return false;
} }
bool add = false; bool add = false;
if (v.type() == nFunction && v.lambda.fun->hasFormals()) { if (v.type() == nFunction && v.payload.lambda.fun->hasFormals()) {
for (auto & i : v.lambda.fun->formals->formals) { for (auto & i : v.payload.lambda.fun->formals->formals) {
if (state->symbols[i.name] == "inNixShell") { if (state->symbols[i.name] == "inNixShell") {
add = true; add = true;
break; break;

View file

@ -1238,15 +1238,15 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs)
xml.writeEmptyElement("meta", attrs2); xml.writeEmptyElement("meta", attrs2);
} else if (v->type() == nInt) { } else if (v->type() == nInt) {
attrs2["type"] = "int"; attrs2["type"] = "int";
attrs2["value"] = fmt("%1%", v->integer); attrs2["value"] = fmt("%1%", v->integer());
xml.writeEmptyElement("meta", attrs2); xml.writeEmptyElement("meta", attrs2);
} else if (v->type() == nFloat) { } else if (v->type() == nFloat) {
attrs2["type"] = "float"; attrs2["type"] = "float";
attrs2["value"] = fmt("%1%", v->fpoint); attrs2["value"] = fmt("%1%", v->fpoint());
xml.writeEmptyElement("meta", attrs2); xml.writeEmptyElement("meta", attrs2);
} else if (v->type() == nBool) { } else if (v->type() == nBool) {
attrs2["type"] = "bool"; attrs2["type"] = "bool";
attrs2["value"] = v->boolean ? "true" : "false"; attrs2["value"] = v->boolean() ? "true" : "false";
xml.writeEmptyElement("meta", attrs2); xml.writeEmptyElement("meta", attrs2);
} else if (v->type() == nList) { } else if (v->type() == nList) {
attrs2["type"] = "strings"; attrs2["type"] = "strings";
@ -1260,13 +1260,11 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs)
} else if (v->type() == nAttrs) { } else if (v->type() == nAttrs) {
attrs2["type"] = "strings"; attrs2["type"] = "strings";
XMLOpenElement m(xml, "meta", attrs2); XMLOpenElement m(xml, "meta", attrs2);
Bindings & attrs = *v->attrs; for (auto & i : *v->attrs()) {
for (auto &i : attrs) { if (i.value->type() != nString) continue;
Attr & a(*attrs.find(i.name));
if(a.value->type() != nString) continue;
XMLAttrs attrs3; XMLAttrs attrs3;
attrs3["type"] = globals.state->symbols[i.name]; attrs3["type"] = globals.state->symbols[i.name];
attrs3["value"] = a.value->c_str(); attrs3["value"] = i.value->c_str();
xml.writeEmptyElement("string", attrs3); xml.writeEmptyElement("string", attrs3);
} }
} }

View file

@ -138,9 +138,9 @@ bool createUserEnv(EvalState & state, PackageInfos & elems,
debug("evaluating user environment builder"); debug("evaluating user environment builder");
state.forceValue(topLevel, topLevel.determinePos(noPos)); state.forceValue(topLevel, topLevel.determinePos(noPos));
NixStringContext context; NixStringContext context;
Attr & aDrvPath(*topLevel.attrs->find(state.sDrvPath)); auto & 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)); auto & 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. */

View file

@ -93,14 +93,14 @@ struct CmdBundle : InstallableValueCommand
if (!evalState->isDerivation(*vRes)) if (!evalState->isDerivation(*vRes))
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 attr1 = vRes->attrs->get(evalState->sDrvPath); auto attr1 = vRes->attrs()->get(evalState->sDrvPath);
if (!attr1) if (!attr1)
throw Error("the bundler '%s' does not produce a derivation", bundler.what()); throw Error("the bundler '%s' does not produce a derivation", bundler.what());
NixStringContext context2; NixStringContext 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());
@ -116,7 +116,7 @@ struct CmdBundle : InstallableValueCommand
auto outPathS = store->printStorePath(outPath); auto outPathS = store->printStorePath(outPath);
if (!outLink) { if (!outLink) {
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, "");

View file

@ -89,7 +89,7 @@ struct CmdEval : MixJSON, InstallableValueCommand, MixReadOnlyOption
else if (v.type() == nAttrs) { else if (v.type() == nAttrs) {
if (mkdir(path.c_str(), 0777) == -1) if (mkdir(path.c_str(), 0777) == -1)
throw SysError("creating directory '%s'", path); throw SysError("creating directory '%s'", path);
for (auto & attr : *v.attrs) { for (auto & attr : *v.attrs()) {
std::string_view name = state->symbols[attr.name]; std::string_view name = state->symbols[attr.name];
try { try {
if (name == "." || name == "..") if (name == "." || name == "..")

View file

@ -169,7 +169,7 @@ static void enumerateOutputs(EvalState & state, Value & vFlake,
auto pos = vFlake.determinePos(noPos); auto pos = vFlake.determinePos(noPos);
state.forceAttrs(vFlake, pos, "while evaluating a flake to get its outputs"); state.forceAttrs(vFlake, pos, "while evaluating a flake to get its outputs");
auto aOutputs = vFlake.attrs->get(state.symbols.create("outputs")); auto aOutputs = vFlake.attrs()->get(state.symbols.create("outputs"));
assert(aOutputs); assert(aOutputs);
state.forceAttrs(*aOutputs->value, pos, "while evaluating the outputs of a flake"); state.forceAttrs(*aOutputs->value, pos, "while evaluating the outputs of a flake");
@ -179,10 +179,10 @@ static void enumerateOutputs(EvalState & state, Value & vFlake,
/* Hack: ensure that hydraJobs is evaluated before anything /* Hack: ensure that hydraJobs is evaluated before anything
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(state.symbols[attr->name], *attr->value, attr->pos); callback(state.symbols[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(state.symbols[attr.name], *attr.value, attr.pos); callback(state.symbols[attr.name], *attr.value, attr.pos);
} }
@ -451,10 +451,10 @@ struct CmdFlakeCheck : FlakeCommand
if (!v.isLambda()) { if (!v.isLambda()) {
throw Error("overlay is not a function, but %s instead", showType(v)); throw Error("overlay is not a function, but %s instead", showType(v));
} }
if (v.lambda.fun->hasFormals() if (v.payload.lambda.fun->hasFormals()
|| !argHasName(v.lambda.fun->arg, "final")) || !argHasName(v.payload.lambda.fun->arg, "final"))
throw Error("overlay does not take an argument named 'final'"); throw Error("overlay does not take an argument named 'final'");
auto body = dynamic_cast<ExprLambda *>(v.lambda.fun->body); auto body = dynamic_cast<ExprLambda *>(v.payload.lambda.fun->body);
if (!body if (!body
|| body->hasFormals() || body->hasFormals()
|| !argHasName(body->arg, "prev")) || !argHasName(body->arg, "prev"))
@ -489,7 +489,7 @@ struct CmdFlakeCheck : FlakeCommand
if (state->isDerivation(v)) if (state->isDerivation(v))
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 = concatStrings(attrPath, ".", state->symbols[attr.name]); auto attrPath2 = concatStrings(attrPath, ".", state->symbols[attr.name]);
if (state->isDerivation(*attr.value)) { if (state->isDerivation(*attr.value)) {
@ -528,7 +528,7 @@ struct CmdFlakeCheck : FlakeCommand
state->forceAttrs(v, pos, ""); state->forceAttrs(v, pos, "");
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")) {
NixStringContext context; NixStringContext context;
auto path = state->coerceToPath(attr->pos, *attr->value, context, ""); auto path = state->coerceToPath(attr->pos, *attr->value, context, "");
@ -539,12 +539,12 @@ struct CmdFlakeCheck : FlakeCommand
} else } else
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);
for (auto & attr : *v.attrs) { for (auto & attr : *v.attrs()) {
std::string_view name(state->symbols[attr.name]); std::string_view name(state->symbols[attr.name]);
if (name != "path" && name != "description" && name != "welcomeText") if (name != "path" && name != "description" && name != "welcomeText")
throw Error("template '%s' has unsupported attribute '%s'", attrPath, name); throw Error("template '%s' has unsupported attribute '%s'", attrPath, name);
@ -600,12 +600,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()) {
const auto & attr_name = state->symbols[attr.name]; const auto & attr_name = state->symbols[attr.name];
checkSystemName(attr_name, attr.pos); checkSystemName(attr_name, attr.pos);
if (checkSystemType(attr_name, attr.pos)) { if (checkSystemType(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, state->symbols[attr2.name]), fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]),
*attr2.value, attr2.pos); *attr2.value, attr2.pos);
@ -622,7 +622,7 @@ 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()) {
const auto & attr_name = state->symbols[attr.name]; const auto & attr_name = state->symbols[attr.name];
checkSystemName(attr_name, attr.pos); checkSystemName(attr_name, attr.pos);
if (checkSystemType(attr_name, attr.pos)) { if (checkSystemType(attr_name, attr.pos)) {
@ -635,12 +635,12 @@ struct CmdFlakeCheck : FlakeCommand
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()) {
const auto & attr_name = state->symbols[attr.name]; const auto & attr_name = state->symbols[attr.name];
checkSystemName(attr_name, attr.pos); checkSystemName(attr_name, attr.pos);
if (checkSystemType(attr_name, attr.pos)) { if (checkSystemType(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, state->symbols[attr2.name]), fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]),
*attr2.value, attr2.pos); *attr2.value, attr2.pos);
@ -650,12 +650,12 @@ struct CmdFlakeCheck : FlakeCommand
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()) {
const auto & attr_name = state->symbols[attr.name]; const auto & attr_name = state->symbols[attr.name];
checkSystemName(attr_name, attr.pos); checkSystemName(attr_name, attr.pos);
if (checkSystemType(attr_name, attr.pos)) { if (checkSystemType(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, state->symbols[attr2.name]), fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]),
*attr2.value, attr2.pos); *attr2.value, attr2.pos);
@ -665,7 +665,7 @@ struct CmdFlakeCheck : FlakeCommand
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()) {
const auto & attr_name = state->symbols[attr.name]; const auto & attr_name = state->symbols[attr.name];
checkSystemName(attr_name, attr.pos); checkSystemName(attr_name, attr.pos);
if (checkSystemType(attr_name, attr.pos)) { if (checkSystemType(attr_name, attr.pos)) {
@ -678,7 +678,7 @@ struct CmdFlakeCheck : FlakeCommand
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()) {
const auto & attr_name = state->symbols[attr.name]; const auto & attr_name = state->symbols[attr.name];
checkSystemName(attr_name, attr.pos); checkSystemName(attr_name, attr.pos);
if (checkSystemType(attr_name, attr.pos) ) { if (checkSystemType(attr_name, attr.pos) ) {
@ -691,7 +691,7 @@ struct CmdFlakeCheck : FlakeCommand
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(state->symbols[attr.name], attr.pos); checkSystemName(state->symbols[attr.name], attr.pos);
checkSystemType(state->symbols[attr.name], attr.pos); checkSystemType(state->symbols[attr.name], attr.pos);
// FIXME: do getDerivations? // FIXME: do getDerivations?
@ -703,7 +703,7 @@ struct CmdFlakeCheck : FlakeCommand
else if (name == "overlays") { else if (name == "overlays") {
state->forceAttrs(vOutput, pos, ""); state->forceAttrs(vOutput, pos, "");
for (auto & attr : *vOutput.attrs) for (auto & attr : *vOutput.attrs())
checkOverlay(fmt("%s.%s", name, state->symbols[attr.name]), checkOverlay(fmt("%s.%s", name, state->symbols[attr.name]),
*attr.value, attr.pos); *attr.value, attr.pos);
} }
@ -713,14 +713,14 @@ struct CmdFlakeCheck : FlakeCommand
else if (name == "nixosModules") { else if (name == "nixosModules") {
state->forceAttrs(vOutput, pos, ""); state->forceAttrs(vOutput, pos, "");
for (auto & attr : *vOutput.attrs) for (auto & attr : *vOutput.attrs())
checkModule(fmt("%s.%s", name, state->symbols[attr.name]), checkModule(fmt("%s.%s", name, state->symbols[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, state->symbols[attr.name]), checkNixOSConfiguration(fmt("%s.%s", name, state->symbols[attr.name]),
*attr.value, attr.pos); *attr.value, attr.pos);
} }
@ -733,14 +733,14 @@ struct CmdFlakeCheck : FlakeCommand
else if (name == "templates") { else if (name == "templates") {
state->forceAttrs(vOutput, pos, ""); state->forceAttrs(vOutput, pos, "");
for (auto & attr : *vOutput.attrs) for (auto & attr : *vOutput.attrs())
checkTemplate(fmt("%s.%s", name, state->symbols[attr.name]), checkTemplate(fmt("%s.%s", name, state->symbols[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()) {
const auto & attr_name = state->symbols[attr.name]; const auto & attr_name = state->symbols[attr.name];
checkSystemName(attr_name, attr.pos); checkSystemName(attr_name, attr.pos);
if (checkSystemType(attr_name, attr.pos)) { if (checkSystemType(attr_name, attr.pos)) {
@ -753,12 +753,12 @@ struct CmdFlakeCheck : FlakeCommand
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()) {
const auto & attr_name = state->symbols[attr.name]; const auto & attr_name = state->symbols[attr.name];
checkSystemName(attr_name, attr.pos); checkSystemName(attr_name, attr.pos);
if (checkSystemType(attr_name, attr.pos)) { if (checkSystemType(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, state->symbols[attr2.name]), fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]),
*attr2.value, attr2.pos); *attr2.value, attr2.pos);

View file

@ -260,7 +260,7 @@ static void showHelp(std::vector<std::string> subcommand, NixArgs & toplevel)
state.callFunction(*vGenerateManpage, state.getBuiltin("false"), *vRes, noPos); state.callFunction(*vGenerateManpage, state.getBuiltin("false"), *vRes, noPos);
state.callFunction(*vRes, *vDump, *vRes, noPos); state.callFunction(*vRes, *vDump, *vRes, noPos);
auto attr = vRes->attrs->get(state.symbols.create(mdName + ".md")); auto attr = vRes->attrs()->get(state.symbols.create(mdName + ".md"));
if (!attr) if (!attr)
throw UsageError("Nix has no subcommand '%s'", concatStringsSep("", subcommand)); throw UsageError("Nix has no subcommand '%s'", concatStringsSep("", subcommand));
@ -406,11 +406,10 @@ void mainWrapped(int argc, char * * argv)
auto res = nlohmann::json::object(); auto res = nlohmann::json::object();
res["builtins"] = ({ res["builtins"] = ({
auto builtinsJson = nlohmann::json::object(); auto builtinsJson = nlohmann::json::object();
auto builtins = state.baseEnv.values[0]->attrs; for (auto & builtin : *state.baseEnv.values[0]->attrs()) {
for (auto & builtin : *builtins) {
auto b = nlohmann::json::object(); auto b = nlohmann::json::object();
if (!builtin.value->isPrimOp()) continue; if (!builtin.value->isPrimOp()) continue;
auto primOp = builtin.value->primOp; auto primOp = builtin.value->primOp();
if (!primOp->doc) continue; if (!primOp->doc) continue;
b["arity"] = primOp->arity; b["arity"] = primOp->arity;
b["args"] = primOp->args; b["args"] = primOp->args;

View file

@ -36,8 +36,8 @@ std::string resolveMirrorUrl(EvalState & state, const std::string & url)
vMirrors); vMirrors);
state.forceAttrs(vMirrors, noPos, "while evaluating the set of all mirrors"); state.forceAttrs(vMirrors, noPos, "while evaluating the set of all mirrors");
auto mirrorList = vMirrors.attrs->find(state.symbols.create(mirrorName)); auto mirrorList = vMirrors.attrs()->get(state.symbols.create(mirrorName));
if (mirrorList == vMirrors.attrs->end()) if (!mirrorList)
throw Error("unknown mirror name '%s'", mirrorName); throw Error("unknown mirror name '%s'", mirrorName);
state.forceList(*mirrorList->value, noPos, "while evaluating one mirror configuration"); state.forceList(*mirrorList->value, noPos, "while evaluating one mirror configuration");
@ -214,7 +214,7 @@ static int main_nix_prefetch_url(int argc, char * * argv)
state->forceAttrs(v, noPos, "while evaluating the source attribute to prefetch"); state->forceAttrs(v, noPos, "while evaluating the source attribute to prefetch");
/* Extract the URL. */ /* Extract the URL. */
auto * attr = v.attrs->get(state->symbols.create("urls")); auto * attr = v.attrs()->get(state->symbols.create("urls"));
if (!attr) if (!attr)
throw Error("attribute 'urls' missing"); throw Error("attribute 'urls' missing");
state->forceList(*attr->value, noPos, "while evaluating the urls to prefetch"); state->forceList(*attr->value, noPos, "while evaluating the urls to prefetch");
@ -223,7 +223,7 @@ static int main_nix_prefetch_url(int argc, char * * argv)
url = state->forceString(*attr->value->listElems()[0], noPos, "while evaluating the first url from the urls list"); url = state->forceString(*attr->value->listElems()[0], noPos, "while evaluating the first url from the urls list");
/* Extract the hash mode. */ /* Extract the hash mode. */
auto attr2 = v.attrs->get(state->symbols.create("outputHashMode")); auto attr2 = v.attrs()->get(state->symbols.create("outputHashMode"));
if (!attr2) if (!attr2)
printInfo("warning: this does not look like a fetchurl call"); printInfo("warning: this does not look like a fetchurl call");
else else
@ -231,7 +231,7 @@ static int main_nix_prefetch_url(int argc, char * * argv)
/* Extract the name. */ /* Extract the name. */
if (!name) { if (!name) {
auto attr3 = v.attrs->get(state->symbols.create("name")); auto attr3 = v.attrs()->get(state->symbols.create("name"));
if (!attr3) if (!attr3)
name = state->forceString(*attr3->value, noPos, "while evaluating the name of the source to prefetch"); name = state->forceString(*attr3->value, noPos, "while evaluating the name of the source to prefetch");
} }

View file

@ -80,28 +80,28 @@ namespace nix {
if (arg.type() != nInt) { if (arg.type() != nInt) {
return false; return false;
} }
return arg.integer == v; return arg.integer() == v;
} }
MATCHER_P(IsFloatEq, v, fmt("The float is equal to \"%1%\"", v)) { MATCHER_P(IsFloatEq, v, fmt("The float is equal to \"%1%\"", v)) {
if (arg.type() != nFloat) { if (arg.type() != nFloat) {
return false; return false;
} }
return arg.fpoint == v; return arg.fpoint() == v;
} }
MATCHER(IsTrue, "") { MATCHER(IsTrue, "") {
if (arg.type() != nBool) { if (arg.type() != nBool) {
return false; return false;
} }
return arg.boolean == true; return arg.boolean() == true;
} }
MATCHER(IsFalse, "") { MATCHER(IsFalse, "") {
if (arg.type() != nBool) { if (arg.type() != nBool) {
return false; return false;
} }
return arg.boolean == false; return arg.boolean() == false;
} }
MATCHER_P(IsPathEq, p, fmt("Is a path equal to \"%1%\"", p)) { MATCHER_P(IsPathEq, p, fmt("Is a path equal to \"%1%\"", p)) {
@ -134,8 +134,8 @@ namespace nix {
if (arg.type() != nAttrs) { if (arg.type() != nAttrs) {
*result_listener << "Expected set got " << arg.type(); *result_listener << "Expected set got " << arg.type();
return false; return false;
} else if (arg.attrs->size() != (size_t)n) { } else if (arg.attrs()->size() != (size_t) n) {
*result_listener << "Expected a set with " << n << " attributes but got " << arg.attrs->size(); *result_listener << "Expected a set with " << n << " attributes but got " << arg.attrs()->size();
return false; return false;
} }
return true; return true;

View file

@ -72,7 +72,7 @@ namespace nix {
auto v = eval("builtins.tryEval (throw \"\")"); auto v = eval("builtins.tryEval (throw \"\")");
ASSERT_THAT(v, IsAttrsOfSize(2)); ASSERT_THAT(v, IsAttrsOfSize(2));
auto s = createSymbol("success"); auto s = createSymbol("success");
auto p = v.attrs->get(s); auto p = v.attrs()->get(s);
ASSERT_NE(p, nullptr); ASSERT_NE(p, nullptr);
ASSERT_THAT(*p->value, IsFalse()); ASSERT_THAT(*p->value, IsFalse());
} }
@ -81,11 +81,11 @@ namespace nix {
auto v = eval("builtins.tryEval 123"); auto v = eval("builtins.tryEval 123");
ASSERT_THAT(v, IsAttrs()); ASSERT_THAT(v, IsAttrs());
auto s = createSymbol("success"); auto s = createSymbol("success");
auto p = v.attrs->get(s); auto p = v.attrs()->get(s);
ASSERT_NE(p, nullptr); ASSERT_NE(p, nullptr);
ASSERT_THAT(*p->value, IsTrue()); ASSERT_THAT(*p->value, IsTrue());
s = createSymbol("value"); s = createSymbol("value");
p = v.attrs->get(s); p = v.attrs()->get(s);
ASSERT_NE(p, nullptr); ASSERT_NE(p, nullptr);
ASSERT_THAT(*p->value, IsIntEq(123)); ASSERT_THAT(*p->value, IsIntEq(123));
} }
@ -157,18 +157,18 @@ namespace nix {
auto v = eval(expr); auto v = eval(expr);
ASSERT_THAT(v, IsAttrsOfSize(3)); ASSERT_THAT(v, IsAttrsOfSize(3));
auto file = v.attrs->find(createSymbol("file")); auto file = v.attrs()->find(createSymbol("file"));
ASSERT_NE(file, nullptr); ASSERT_NE(file, nullptr);
ASSERT_THAT(*file->value, IsString()); ASSERT_THAT(*file->value, IsString());
auto s = baseNameOf(file->value->string_view()); auto s = baseNameOf(file->value->string_view());
ASSERT_EQ(s, "foo.nix"); ASSERT_EQ(s, "foo.nix");
auto line = v.attrs->find(createSymbol("line")); auto line = v.attrs()->find(createSymbol("line"));
ASSERT_NE(line, nullptr); ASSERT_NE(line, nullptr);
state.forceValue(*line->value, noPos); state.forceValue(*line->value, noPos);
ASSERT_THAT(*line->value, IsIntEq(4)); ASSERT_THAT(*line->value, IsIntEq(4));
auto column = v.attrs->find(createSymbol("column")); auto column = v.attrs()->find(createSymbol("column"));
ASSERT_NE(column, nullptr); ASSERT_NE(column, nullptr);
state.forceValue(*column->value, noPos); state.forceValue(*column->value, noPos);
ASSERT_THAT(*column->value, IsIntEq(3)); ASSERT_THAT(*column->value, IsIntEq(3));
@ -202,14 +202,14 @@ namespace nix {
TEST_F(PrimOpTest, removeAttrsRetains) { TEST_F(PrimOpTest, removeAttrsRetains) {
auto v = eval("builtins.removeAttrs { x = 1; y = 2; } [\"x\"]"); auto v = eval("builtins.removeAttrs { x = 1; y = 2; } [\"x\"]");
ASSERT_THAT(v, IsAttrsOfSize(1)); ASSERT_THAT(v, IsAttrsOfSize(1));
ASSERT_NE(v.attrs->find(createSymbol("y")), nullptr); ASSERT_NE(v.attrs()->find(createSymbol("y")), nullptr);
} }
TEST_F(PrimOpTest, listToAttrsEmptyList) { TEST_F(PrimOpTest, listToAttrsEmptyList) {
auto v = eval("builtins.listToAttrs []"); auto v = eval("builtins.listToAttrs []");
ASSERT_THAT(v, IsAttrsOfSize(0)); ASSERT_THAT(v, IsAttrsOfSize(0));
ASSERT_EQ(v.type(), nAttrs); ASSERT_EQ(v.type(), nAttrs);
ASSERT_EQ(v.attrs->size(), 0); ASSERT_EQ(v.attrs()->size(), 0);
} }
TEST_F(PrimOpTest, listToAttrsNotFieldName) { TEST_F(PrimOpTest, listToAttrsNotFieldName) {
@ -219,7 +219,7 @@ namespace nix {
TEST_F(PrimOpTest, listToAttrs) { TEST_F(PrimOpTest, listToAttrs) {
auto v = eval("builtins.listToAttrs [ { name = \"key\"; value = 123; } ]"); auto v = eval("builtins.listToAttrs [ { name = \"key\"; value = 123; } ]");
ASSERT_THAT(v, IsAttrsOfSize(1)); ASSERT_THAT(v, IsAttrsOfSize(1));
auto key = v.attrs->find(createSymbol("key")); auto key = v.attrs()->find(createSymbol("key"));
ASSERT_NE(key, nullptr); ASSERT_NE(key, nullptr);
ASSERT_THAT(*key->value, IsIntEq(123)); ASSERT_THAT(*key->value, IsIntEq(123));
} }
@ -227,7 +227,7 @@ namespace nix {
TEST_F(PrimOpTest, intersectAttrs) { TEST_F(PrimOpTest, intersectAttrs) {
auto v = eval("builtins.intersectAttrs { a = 1; b = 2; } { b = 3; c = 4; }"); auto v = eval("builtins.intersectAttrs { a = 1; b = 2; } { b = 3; c = 4; }");
ASSERT_THAT(v, IsAttrsOfSize(1)); ASSERT_THAT(v, IsAttrsOfSize(1));
auto b = v.attrs->find(createSymbol("b")); auto b = v.attrs()->find(createSymbol("b"));
ASSERT_NE(b, nullptr); ASSERT_NE(b, nullptr);
ASSERT_THAT(*b->value, IsIntEq(3)); ASSERT_THAT(*b->value, IsIntEq(3));
} }
@ -243,11 +243,11 @@ namespace nix {
auto v = eval("builtins.functionArgs ({ x, y ? 123}: 1)"); auto v = eval("builtins.functionArgs ({ x, y ? 123}: 1)");
ASSERT_THAT(v, IsAttrsOfSize(2)); ASSERT_THAT(v, IsAttrsOfSize(2));
auto x = v.attrs->find(createSymbol("x")); auto x = v.attrs()->find(createSymbol("x"));
ASSERT_NE(x, nullptr); ASSERT_NE(x, nullptr);
ASSERT_THAT(*x->value, IsFalse()); ASSERT_THAT(*x->value, IsFalse());
auto y = v.attrs->find(createSymbol("y")); auto y = v.attrs()->find(createSymbol("y"));
ASSERT_NE(y, nullptr); ASSERT_NE(y, nullptr);
ASSERT_THAT(*y->value, IsTrue()); ASSERT_THAT(*y->value, IsTrue());
} }
@ -256,13 +256,13 @@ namespace nix {
auto v = eval("builtins.mapAttrs (name: value: value * 10) { a = 1; b = 2; }"); auto v = eval("builtins.mapAttrs (name: value: value * 10) { a = 1; b = 2; }");
ASSERT_THAT(v, IsAttrsOfSize(2)); ASSERT_THAT(v, IsAttrsOfSize(2));
auto a = v.attrs->find(createSymbol("a")); auto a = v.attrs()->find(createSymbol("a"));
ASSERT_NE(a, nullptr); ASSERT_NE(a, nullptr);
ASSERT_THAT(*a->value, IsThunk()); ASSERT_THAT(*a->value, IsThunk());
state.forceValue(*a->value, noPos); state.forceValue(*a->value, noPos);
ASSERT_THAT(*a->value, IsIntEq(10)); ASSERT_THAT(*a->value, IsIntEq(10));
auto b = v.attrs->find(createSymbol("b")); auto b = v.attrs()->find(createSymbol("b"));
ASSERT_NE(b, nullptr); ASSERT_NE(b, nullptr);
ASSERT_THAT(*b->value, IsThunk()); ASSERT_THAT(*b->value, IsThunk());
state.forceValue(*b->value, noPos); state.forceValue(*b->value, noPos);
@ -410,13 +410,13 @@ namespace nix {
auto v = eval("builtins.partition (x: x > 10) [1 23 9 3 42]"); auto v = eval("builtins.partition (x: x > 10) [1 23 9 3 42]");
ASSERT_THAT(v, IsAttrsOfSize(2)); ASSERT_THAT(v, IsAttrsOfSize(2));
auto right = v.attrs->get(createSymbol("right")); auto right = v.attrs()->get(createSymbol("right"));
ASSERT_NE(right, nullptr); ASSERT_NE(right, nullptr);
ASSERT_THAT(*right->value, IsListOfSize(2)); ASSERT_THAT(*right->value, IsListOfSize(2));
ASSERT_THAT(*right->value->listElems()[0], IsIntEq(23)); ASSERT_THAT(*right->value->listElems()[0], IsIntEq(23));
ASSERT_THAT(*right->value->listElems()[1], IsIntEq(42)); ASSERT_THAT(*right->value->listElems()[1], IsIntEq(42));
auto wrong = v.attrs->get(createSymbol("wrong")); auto wrong = v.attrs()->get(createSymbol("wrong"));
ASSERT_NE(wrong, nullptr); ASSERT_NE(wrong, nullptr);
ASSERT_EQ(wrong->value->type(), nList); ASSERT_EQ(wrong->value->type(), nList);
ASSERT_EQ(wrong->value->listSize(), 3); ASSERT_EQ(wrong->value->listSize(), 3);
@ -641,14 +641,14 @@ namespace nix {
auto v = eval("derivation"); auto v = eval("derivation");
ASSERT_EQ(v.type(), nFunction); ASSERT_EQ(v.type(), nFunction);
ASSERT_TRUE(v.isLambda()); ASSERT_TRUE(v.isLambda());
ASSERT_NE(v.lambda.fun, nullptr); ASSERT_NE(v.payload.lambda.fun, nullptr);
ASSERT_TRUE(v.lambda.fun->hasFormals()); ASSERT_TRUE(v.payload.lambda.fun->hasFormals());
} }
TEST_F(PrimOpTest, currentTime) { TEST_F(PrimOpTest, currentTime) {
auto v = eval("builtins.currentTime"); auto v = eval("builtins.currentTime");
ASSERT_EQ(v.type(), nInt); ASSERT_EQ(v.type(), nInt);
ASSERT_TRUE(v.integer > 0); ASSERT_TRUE(v.integer() > 0);
} }
TEST_F(PrimOpTest, splitVersion) { TEST_F(PrimOpTest, splitVersion) {
@ -709,11 +709,11 @@ namespace nix {
auto v = eval(expr); auto v = eval(expr);
ASSERT_THAT(v, IsAttrsOfSize(2)); ASSERT_THAT(v, IsAttrsOfSize(2));
auto name = v.attrs->find(createSymbol("name")); auto name = v.attrs()->find(createSymbol("name"));
ASSERT_TRUE(name); ASSERT_TRUE(name);
ASSERT_THAT(*name->value, IsStringEq(expectedName)); ASSERT_THAT(*name->value, IsStringEq(expectedName));
auto version = v.attrs->find(createSymbol("version")); auto version = v.attrs()->find(createSymbol("version"));
ASSERT_TRUE(version); ASSERT_TRUE(version);
ASSERT_THAT(*version->value, IsStringEq(expectedVersion)); ASSERT_THAT(*version->value, IsStringEq(expectedVersion));
} }

View file

@ -62,11 +62,11 @@ namespace nix {
TEST_F(TrivialExpressionTest, updateAttrs) { TEST_F(TrivialExpressionTest, updateAttrs) {
auto v = eval("{ a = 1; } // { b = 2; a = 3; }"); auto v = eval("{ a = 1; } // { b = 2; a = 3; }");
ASSERT_THAT(v, IsAttrsOfSize(2)); ASSERT_THAT(v, IsAttrsOfSize(2));
auto a = v.attrs->find(createSymbol("a")); auto a = v.attrs()->find(createSymbol("a"));
ASSERT_NE(a, nullptr); ASSERT_NE(a, nullptr);
ASSERT_THAT(*a->value, IsIntEq(3)); ASSERT_THAT(*a->value, IsIntEq(3));
auto b = v.attrs->find(createSymbol("b")); auto b = v.attrs()->find(createSymbol("b"));
ASSERT_NE(b, nullptr); ASSERT_NE(b, nullptr);
ASSERT_THAT(*b->value, IsIntEq(2)); ASSERT_THAT(*b->value, IsIntEq(2));
} }
@ -151,7 +151,7 @@ namespace nix {
auto v = eval(expr); auto v = eval(expr);
ASSERT_THAT(v, IsAttrsOfSize(1)); ASSERT_THAT(v, IsAttrsOfSize(1));
auto a = v.attrs->find(createSymbol("a")); auto a = v.attrs()->find(createSymbol("a"));
ASSERT_NE(a, nullptr); ASSERT_NE(a, nullptr);
ASSERT_THAT(*a->value, IsThunk()); ASSERT_THAT(*a->value, IsThunk());
@ -159,11 +159,11 @@ namespace nix {
ASSERT_THAT(*a->value, IsAttrsOfSize(2)); ASSERT_THAT(*a->value, IsAttrsOfSize(2));
auto b = a->value->attrs->find(createSymbol("b")); auto b = a->value->attrs()->find(createSymbol("b"));
ASSERT_NE(b, nullptr); ASSERT_NE(b, nullptr);
ASSERT_THAT(*b->value, IsIntEq(1)); ASSERT_THAT(*b->value, IsIntEq(1));
auto c = a->value->attrs->find(createSymbol("c")); auto c = a->value->attrs()->find(createSymbol("c"));
ASSERT_NE(c, nullptr); ASSERT_NE(c, nullptr);
ASSERT_THAT(*c->value, IsIntEq(2)); ASSERT_THAT(*c->value, IsIntEq(2));
} }
@ -185,7 +185,7 @@ namespace nix {
TEST_F(TrivialExpressionTest, bindOr) { TEST_F(TrivialExpressionTest, bindOr) {
auto v = eval("{ or = 1; }"); auto v = eval("{ or = 1; }");
ASSERT_THAT(v, IsAttrsOfSize(1)); ASSERT_THAT(v, IsAttrsOfSize(1));
auto b = v.attrs->find(createSymbol("or")); auto b = v.attrs()->find(createSymbol("or"));
ASSERT_NE(b, nullptr); ASSERT_NE(b, nullptr);
ASSERT_THAT(*b->value, IsIntEq(1)); ASSERT_THAT(*b->value, IsIntEq(1));
} }