Skip to content

Commit

Permalink
POC implementation of lazy attribute names
Browse files Browse the repository at this point in the history
This makes the following work:

  nix-instantiate --eval -E '(throw "" // { x = 0; }).x'

Does not work yet for e.g.

  nix-instantiate --eval -E 'let a = (throw "" // { x = 0; }); in a.x'
  • Loading branch information
infinisil committed Sep 28, 2020
1 parent 5080d4e commit afeda4f
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 13 deletions.
47 changes: 36 additions & 11 deletions src/libexpr/eval.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1104,13 +1104,13 @@ static string showAttrPath(EvalState & state, Env & env, const AttrPath & attrPa

unsigned long nrLookups = 0;

void ExprSelect::eval(EvalState & state, Env & env, Value & v)
bool Expr::select(EvalState & state, Env & env, const AttrPath & attrPath, Expr * def, const Pos & pos, bool required, Value & v)
{
Value vTmp;
Pos * pos2 = 0;
Value * vAttrs = &vTmp;

e->eval(state, env, vTmp);
this->eval(state, env, vTmp);

try {

Expand All @@ -1124,19 +1124,24 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
(j = vAttrs->attrs->find(name)) == vAttrs->attrs->end())
{
def->eval(state, env, v);
return;
return true;
}
} else {
state.forceAttrs(*vAttrs, pos);
if ((j = vAttrs->attrs->find(name)) == vAttrs->attrs->end())
throwEvalError(pos, "attribute '%1%' missing", name);
if ((j = vAttrs->attrs->find(name)) == vAttrs->attrs->end()) {
if (required) {
throwEvalError(pos, "attribute '%1%' missing", name);
} else {
return false;
}
}
}
vAttrs = j->value;
pos2 = j->pos;
if (state.countCalls && pos2) state.attrSelects[*pos2]++;
}

state.forceValue(*vAttrs, ( pos2 != NULL ? *pos2 : this->pos ) );
state.forceValue(*vAttrs, ( pos2 != NULL ? *pos2 : pos ) );

} catch (Error & e) {
if (pos2 && pos2->file != state.sDerivationNix)
Expand All @@ -1146,15 +1151,20 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
}

v = *vAttrs;
return true;
}

void ExprSelect::eval(EvalState & state, Env & env, Value & v)
{
e->select(state, env, attrPath, def, pos, true, v);
}

void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v)
bool Expr::hasAttr(EvalState & state, Env & env, const AttrPath & attrPath)
{
Value vTmp;
Value * vAttrs = &vTmp;

e->eval(state, env, vTmp);
this->eval(state, env, vTmp);

for (auto & i : attrPath) {
state.forceValue(*vAttrs);
Expand All @@ -1163,16 +1173,19 @@ void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v)
if (vAttrs->type != tAttrs ||
(j = vAttrs->attrs->find(name)) == vAttrs->attrs->end())
{
mkBool(v, false);
return;
return false;
} else {
vAttrs = j->value;
}
}

mkBool(v, true);
return true;
}

void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v)
{
mkBool(v, e->hasAttr(state, env, attrPath));
}

void ExprLambda::eval(EvalState & state, Env & env, Value & v)
{
Expand Down Expand Up @@ -1449,8 +1462,20 @@ void ExprOpImpl::eval(EvalState & state, Env & env, Value & v)
}


bool ExprOpUpdate::hasAttr(EvalState & state, Env & env, const AttrPath & attrPath)
{
return e2->hasAttr(state, env, attrPath) || e1->hasAttr(state, env, attrPath);
}


bool ExprOpUpdate::select(EvalState & state, Env & env, const AttrPath & attrPath, Expr * def, const Pos & pos, bool required, Value & v)
{
return e2->select(state, env, attrPath, def, pos, false, v) || e1->select(state, env, attrPath, def, pos, required, v);
}

void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v)
{

Value v1, v2;
state.evalAttrs(env, e1, v1);
state.evalAttrs(env, e2, v2);
Expand Down
2 changes: 1 addition & 1 deletion src/libexpr/eval.hh
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ private:

friend struct ExprOpUpdate;
friend struct ExprOpConcatLists;
friend struct ExprSelect;
friend bool Expr::select(EvalState & state, Env & env, const AttrPath & attrPath, Expr * def, const Pos & pos, bool required, Value & v);
friend void prim_getAttr(EvalState & state, const Pos & pos, Value * * args, Value & v);
friend void prim_match(EvalState & state, const Pos & pos, Value * * args, Value & v);
};
Expand Down
25 changes: 24 additions & 1 deletion src/libexpr/nixexpr.hh
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ struct Expr
virtual void eval(EvalState & state, Env & env, Value & v);
virtual Value * maybeThunk(EvalState & state, Env & env);
virtual void setName(Symbol & name);
virtual bool hasAttr(EvalState & state, Env & env, const AttrPath & attrPath);
virtual bool select(EvalState & state, Env & env, const AttrPath & attrPath, Expr * def, const Pos & pos, bool required, Value & v);
};

std::ostream & operator << (std::ostream & str, const Expr & e);
Expand Down Expand Up @@ -311,9 +313,30 @@ MakeBinOp(ExprOpNEq, "!=")
MakeBinOp(ExprOpAnd, "&&")
MakeBinOp(ExprOpOr, "||")
MakeBinOp(ExprOpImpl, "->")
MakeBinOp(ExprOpUpdate, "//")
//MakeBinOp(ExprOpUpdate, "//")
MakeBinOp(ExprOpConcatLists, "++")


struct ExprOpUpdate : Expr
{
Pos pos;
Expr * e1, * e2;
ExprOpUpdate(Expr * e1, Expr * e2) : e1(e1), e2(e2) { };
ExprOpUpdate(const Pos & pos, Expr * e1, Expr * e2) : pos(pos), e1(e1), e2(e2) { };
void show(std::ostream & str) const
{
str << "(" << *e1 << " " "//" " " << *e2 << ")";
}
void bindVars(const StaticEnv & env)
{
e1->bindVars(env); e2->bindVars(env);
}
void eval(EvalState & state, Env & env, Value & v);

bool hasAttr(EvalState & state, Env & env, const AttrPath & attrPath);
bool select(EvalState & state, Env & env, const AttrPath & attrPath, Expr * def, const Pos & pos, bool required, Value & v);
};

struct ExprConcatStrings : Expr
{
Pos pos;
Expand Down

0 comments on commit afeda4f

Please sign in to comment.