* New language feature: domain checks, which check whether a function
argument has a valid value, i.e., is in a certain domain. E.g., { foo : [true false] , bar : ["a" "b" "c"] }: ... This previously could be done using assertions, but domain checks will allow the buildfarm to automatically extract the configuration space from functions.
This commit is contained in:
parent
88acffa20a
commit
f4c5531d92
4 changed files with 40 additions and 13 deletions
|
@ -22,7 +22,8 @@ void EvalState::addPrimOp(const string & name,
|
||||||
|
|
||||||
|
|
||||||
/* Substitute an argument set into the body of a function. */
|
/* Substitute an argument set into the body of a function. */
|
||||||
static Expr substArgs(Expr body, ATermList formals, Expr arg)
|
static Expr substArgs(EvalState & state,
|
||||||
|
Expr body, ATermList formals, Expr arg)
|
||||||
{
|
{
|
||||||
unsigned int nrFormals = ATgetLength(formals);
|
unsigned int nrFormals = ATgetLength(formals);
|
||||||
ATermMap subs(nrFormals);
|
ATermMap subs(nrFormals);
|
||||||
|
@ -37,17 +38,35 @@ static Expr substArgs(Expr body, ATermList formals, Expr arg)
|
||||||
ATermVector defsUsed;
|
ATermVector defsUsed;
|
||||||
ATermList recAttrs = ATempty;
|
ATermList recAttrs = ATempty;
|
||||||
for (ATermIterator i(formals); i; ++i) {
|
for (ATermIterator i(formals); i; ++i) {
|
||||||
Expr name, def; DefaultValue def2; ATerm dummy;
|
Expr name, def;
|
||||||
if (!matchFormal(*i, name, dummy, def2)) abort(); /* can't happen */
|
ValidValues valids2;
|
||||||
if (!matchDefaultValue(def2, def)) def = 0;
|
DefaultValue def2;
|
||||||
if (subs[name] == 0) {
|
if (!matchFormal(*i, name, valids2, def2)) abort(); /* can't happen */
|
||||||
|
|
||||||
|
Expr value = subs[name];
|
||||||
|
|
||||||
|
if (value == 0) {
|
||||||
|
if (!matchDefaultValue(def2, def)) def = 0;
|
||||||
if (def == 0) throw TypeError(format("the argument named `%1%' required by the function is missing")
|
if (def == 0) throw TypeError(format("the argument named `%1%' required by the function is missing")
|
||||||
% aterm2String(name));
|
% aterm2String(name));
|
||||||
|
value = def;
|
||||||
defsUsed.push_back(name);
|
defsUsed.push_back(name);
|
||||||
recAttrs = ATinsert(recAttrs, makeBind(name, def, makeNoPos()));
|
recAttrs = ATinsert(recAttrs, makeBind(name, def, makeNoPos()));
|
||||||
}
|
}
|
||||||
/* !!! check that the argument are in the valid values list,
|
|
||||||
if present */
|
ATermList valids;
|
||||||
|
if (matchValidValues(valids2, valids)) {
|
||||||
|
bool found = false;
|
||||||
|
for (ATermIterator j(valids); j; ++j) {
|
||||||
|
Expr v = evalExpr(state, *j);
|
||||||
|
if (value == v) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) throw TypeError(format("the argument named `%1%' has an illegal value")
|
||||||
|
% aterm2String(name));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Make a recursive attribute set out of the (argument-name,
|
/* Make a recursive attribute set out of the (argument-name,
|
||||||
|
@ -340,7 +359,7 @@ Expr evalExpr2(EvalState & state, Expr e)
|
||||||
else if (matchFunction(e1, formals, e4, pos)) {
|
else if (matchFunction(e1, formals, e4, pos)) {
|
||||||
e2 = evalExpr(state, e2);
|
e2 = evalExpr(state, e2);
|
||||||
try {
|
try {
|
||||||
return evalExpr(state, substArgs(e4, formals, e2));
|
return evalExpr(state, substArgs(state, e4, formals, e2));
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
e.addPrefix(format("while evaluating the function at %1%:\n")
|
e.addPrefix(format("while evaluating the function at %1%:\n")
|
||||||
% showPos(pos));
|
% showPos(pos));
|
||||||
|
|
|
@ -220,15 +220,16 @@ static void checkVarDefs2(set<Expr> & done, const ATermMap & defs, Expr e)
|
||||||
else if (matchFunction(e, formals, body, pos)) {
|
else if (matchFunction(e, formals, body, pos)) {
|
||||||
ATermMap defs2(defs);
|
ATermMap defs2(defs);
|
||||||
for (ATermIterator i(formals); i; ++i) {
|
for (ATermIterator i(formals); i; ++i) {
|
||||||
Expr d1, d2;
|
ATerm d1, d2;
|
||||||
if (!matchFormal(*i, name, d1, d2)) abort();
|
if (!matchFormal(*i, name, d1, d2)) abort();
|
||||||
defs2.set(name, (ATerm) ATempty);
|
defs2.set(name, (ATerm) ATempty);
|
||||||
}
|
}
|
||||||
for (ATermIterator i(formals); i; ++i) {
|
for (ATermIterator i(formals); i; ++i) {
|
||||||
Expr dummy, deflt;
|
ATerm valids, deflt;
|
||||||
set<Expr> done2;
|
set<Expr> done2;
|
||||||
if (matchFormal(*i, name, dummy, deflt)) /* !!! check dummy */
|
matchFormal(*i, name, valids, deflt);
|
||||||
checkVarDefs2(done2, defs2, deflt);
|
checkVarDefs2(done, defs, valids);
|
||||||
|
checkVarDefs2(done2, defs2, deflt);
|
||||||
}
|
}
|
||||||
set<Expr> done2;
|
set<Expr> done2;
|
||||||
checkVarDefs2(done2, defs2, body);
|
checkVarDefs2(done2, defs2, body);
|
||||||
|
|
|
@ -206,7 +206,7 @@ formals
|
||||||
|
|
||||||
formal
|
formal
|
||||||
: ID { $$ = makeFormal($1, makeUnrestrictedValues(), makeNoDefaultValue()); }
|
: ID { $$ = makeFormal($1, makeUnrestrictedValues(), makeNoDefaultValue()); }
|
||||||
// | ID ':' '[' expr_list ']' { $$ = makeDefFormal($1, $3); }
|
| ID ':' '[' expr_list ']' { $$ = makeFormal($1, makeValidValues($4), makeNoDefaultValue()); }
|
||||||
| ID '?' expr { $$ = makeFormal($1, makeUnrestrictedValues(), makeDefaultValue($3)); }
|
| ID '?' expr { $$ = makeFormal($1, makeUnrestrictedValues(), makeDefaultValue($3)); }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
7
tests/lang/parse-fail-undef-var-2.nix
Normal file
7
tests/lang/parse-fail-undef-var-2.nix
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
let {
|
||||||
|
|
||||||
|
f = {x, y : ["baz" "bar" z "bat"]}: x + y;
|
||||||
|
|
||||||
|
body = f {x = "foo"; y = "bar";};
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue