summary refs log tree commit diff
path: root/src/libexpr
diff options
context:
space:
mode:
authorEelco Dolstra <e.dolstra@tudelft.nl>2010-05-07 12:11:05 +0000
committerEelco Dolstra <e.dolstra@tudelft.nl>2010-05-07 12:11:05 +0000
commite2d5e40f4fdd98e5d2ad7b77c00fb97caa3aa259 (patch)
treeb3b8ccf4d534ae55949b02107887574424041668 /src/libexpr
parent84ce7ac76feab6e9a5c074bd1b9550ae543d1db8 (diff)
downloadguix-e2d5e40f4fdd98e5d2ad7b77c00fb97caa3aa259.tar.gz
* Keep track of the source positions of attributes.
Diffstat (limited to 'src/libexpr')
-rw-r--r--src/libexpr/attr-path.cc2
-rw-r--r--src/libexpr/common-opts.cc2
-rw-r--r--src/libexpr/eval.cc59
-rw-r--r--src/libexpr/eval.hh11
-rw-r--r--src/libexpr/get-drvs.cc32
-rw-r--r--src/libexpr/primops.cc66
-rw-r--r--src/libexpr/value-to-xml.cc11
7 files changed, 107 insertions, 76 deletions
diff --git a/src/libexpr/attr-path.cc b/src/libexpr/attr-path.cc
index 769acb6b8c..0660ba1c1e 100644
--- a/src/libexpr/attr-path.cc
+++ b/src/libexpr/attr-path.cc
@@ -48,7 +48,7 @@ void findAlongAttrPath(EvalState & state, const string & attrPath,
             Bindings::iterator a = v.attrs->find(state.symbols.create(attr));
             if (a == v.attrs->end())
                 throw Error(format("attribute `%1%' in selection path `%2%' not found") % attr % curPath);
-            v = a->second;
+            v = a->second.value;
         }
 
         else if (apType == apIndex) {
diff --git a/src/libexpr/common-opts.cc b/src/libexpr/common-opts.cc
index a25317de13..5a4856568b 100644
--- a/src/libexpr/common-opts.cc
+++ b/src/libexpr/common-opts.cc
@@ -20,7 +20,7 @@ bool parseOptionArg(const string & arg, Strings::iterator & i,
     if (i == argsEnd) throw error;
     string value = *i++;
 
-    Value & v(autoArgs[state.symbols.create(name)]);
+    Value & v(autoArgs[state.symbols.create(name)].value);
 
     if (arg == "--arg")
         state.mkThunk_( v, parseExprFromString(state, value, absPath(".")));
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index cb124ab8ba..b8ec410f39 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -44,7 +44,7 @@ std::ostream & operator << (std::ostream & str, Value & v)
     case tAttrs:
         str << "{ ";
         foreach (Bindings::iterator, i, *v.attrs)
-            str << (string) i->first << " = " << i->second << "; ";
+            str << (string) i->first << " = " << i->second.value << "; ";
         str << "}";
         break;
     case tList:
@@ -128,7 +128,7 @@ void EvalState::addConstant(const string & name, Value & v)
     staticBaseEnv.vars[symbols.create(name)] = baseEnvDispl;
     baseEnv.values[baseEnvDispl++] = v;
     string name2 = string(name, 0, 2) == "__" ? string(name, 2) : name;
-    (*baseEnv.values[0].attrs)[symbols.create(name2)] = v;
+    (*baseEnv.values[0].attrs)[symbols.create(name2)].value = v;
 }
 
 
@@ -143,7 +143,7 @@ void EvalState::addPrimOp(const string & name,
     v.primOp.name = strdup(name2.c_str());
     staticBaseEnv.vars[symbols.create(name)] = baseEnvDispl;
     baseEnv.values[baseEnvDispl++] = v;
-    (*baseEnv.values[0].attrs)[symbols.create(name2)] = v;
+    (*baseEnv.values[0].attrs)[symbols.create(name2)].value = v;
 }
 
 
@@ -202,6 +202,11 @@ LocalNoInline(void addErrorPrefix(Error & e, const char * s, const Pos & pos))
     e.addPrefix(format(s) % pos);
 }
 
+LocalNoInline(void addErrorPrefix(Error & e, const char * s, const string & s2, const Pos & pos))
+{
+    e.addPrefix(format(s) % s2 % pos);
+}
+
 
 void mkString(Value & v, const char * s)
 {
@@ -239,7 +244,7 @@ Value * EvalState::lookupVar(Env * env, const VarRef & var)
         while (1) {
             Bindings::iterator j = env->values[0].attrs->find(var.name);
             if (j != env->values[0].attrs->end())
-                return &j->second;
+                return &j->second.value;
             if (env->prevWith == 0)
                 throwEvalError("undefined variable `%1%'", var.name);
             for (unsigned int l = env->prevWith; l; --l, env = env->up) ;
@@ -290,8 +295,11 @@ void EvalState::mkThunk_(Value & v, Expr * expr)
 void EvalState::cloneAttrs(Value & src, Value & dst)
 {
     mkAttrs(dst);
-    foreach (Bindings::iterator, i, *src.attrs)
-        mkCopy((*dst.attrs)[i->first], i->second);
+    foreach (Bindings::iterator, i, *src.attrs) {
+        Attr & a = (*dst.attrs)[i->first];
+        mkCopy(a.value, i->second.value);
+        a.pos = i->second.pos;
+    }
 }
 
 
@@ -414,15 +422,16 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
         /* The recursive attributes are evaluated in the new
            environment. */
         foreach (Attrs::iterator, i, attrs) {
-            Value & v2 = (*v.attrs)[i->first];
-            mkCopy(v2, env2.values[displ]);
+            nix::Attr & a = (*v.attrs)[i->first];
+            mkCopy(a.value, env2.values[displ]);
             mkThunk(env2.values[displ++], env2, i->second.first);
+            a.pos = &i->second.second;
         }
 
         /* The inherited attributes, on the other hand, are
            evaluated in the original environment. */
         foreach (list<VarRef>::iterator, i, inherited) {
-            Value & v2 = (*v.attrs)[i->name];
+            Value & v2 = (*v.attrs)[i->name].value;
             Value * v3 = state.lookupVar(&env, *i);
             mkCopy(v2, *v3);
             mkCopy(env2.values[displ++], *v3);
@@ -432,12 +441,13 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
 
     else {
         foreach (Attrs::iterator, i, attrs) {
-            Value & v2 = (*v.attrs)[i->first];
-            mkThunk(v2, env, i->second.first);
+            nix::Attr & a = (*v.attrs)[i->first];
+            mkThunk(a.value, env, i->second.first);
+            a.pos = &i->second.second;
         }
 
         foreach (list<VarRef>::iterator, i, inherited) {
-            Value & v2 = (*v.attrs)[i->name];
+            Value & v2 = (*v.attrs)[i->name].value;
             mkCopy(v2, *state.lookupVar(&env, *i));
         }
     }
@@ -494,12 +504,13 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
     if (i == v2.attrs->end())
         throwEvalError("attribute `%1%' missing", name);
     try {            
-        state.forceValue(i->second);
+        state.forceValue(i->second.value);
     } catch (Error & e) {
-        addErrorPrefix(e, "while evaluating the attribute `%1%':\n", name);
+        addErrorPrefix(e, "while evaluating the attribute `%1%' at %2%:\n",
+            name, *i->second.pos);
         throw;
     }
-    v = i->second;
+    v = i->second.value;
 }
 
 
@@ -601,7 +612,7 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v)
                 mkThunk(env2.values[displ++], env2, i->def);
             } else {
                 attrsUsed++;
-                mkCopy(env2.values[displ++], j->second);
+                mkCopy(env2.values[displ++], j->second.value);
             }
         }
 
@@ -721,8 +732,11 @@ void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v)
         
     state.evalAttrs(env, e2, v2);
     
-    foreach (Bindings::iterator, i, *v2.attrs)
-        mkCopy((*v.attrs)[i->first], i->second);
+    foreach (Bindings::iterator, i, *v2.attrs) {
+        Attr & a = (*v.attrs)[i->first];
+        mkCopy(a.value, i->second.value);
+        a.pos = i->second.pos;
+    }
 }
 
 
@@ -802,7 +816,7 @@ void EvalState::strictForceValue(Value & v)
     
     if (v.type == tAttrs) {
         foreach (Bindings::iterator, i, *v.attrs)
-            strictForceValue(i->second);
+            strictForceValue(i->second.value);
     }
     
     else if (v.type == tList) {
@@ -887,7 +901,7 @@ bool EvalState::isDerivation(Value & v)
 {
     if (v.type != tAttrs) return false;
     Bindings::iterator i = v.attrs->find(sType);
-    return i != v.attrs->end() && forceStringNoCtx(i->second) == "derivation";
+    return i != v.attrs->end() && forceStringNoCtx(i->second.value) == "derivation";
 }
 
 
@@ -933,7 +947,7 @@ string EvalState::coerceToString(Value & v, PathSet & context,
         Bindings::iterator i = v.attrs->find(sOutPath);
         if (i == v.attrs->end())
             throwTypeError("cannot coerce an attribute set (except a derivation) to a string");
-        return coerceToString(i->second, context, coerceMore, copyToStore);
+        return coerceToString(i->second.value, context, coerceMore, copyToStore);
     }
 
     if (coerceMore) {
@@ -1021,7 +1035,8 @@ bool EvalState::eqValues(Value & v1, Value & v2)
             if (v1.attrs->size() != v2.attrs->size()) return false;
             Bindings::iterator i, j;
             for (i = v1.attrs->begin(), j = v2.attrs->begin(); i != v1.attrs->end(); ++i, ++j)
-                if (i->first != j->first || !eqValues(i->second, j->second)) return false;
+                if (i->first != j->first || !eqValues(i->second.value, j->second.value))
+                    return false;
             return true;
         }
 
diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh
index 6912e22887..1a9862c295 100644
--- a/src/libexpr/eval.hh
+++ b/src/libexpr/eval.hh
@@ -14,8 +14,9 @@ class Hash;
 class EvalState;
 struct Env;
 struct Value;
+struct Attr;
 
-typedef std::map<Symbol, Value> Bindings;
+typedef std::map<Symbol, Attr> Bindings;
 
 
 typedef enum {
@@ -111,6 +112,14 @@ struct Env
 };
 
 
+struct Attr
+{
+    Value value;
+    Pos * pos;
+    Attr() : pos(&noPos) { };
+};
+
+
 static inline void mkInt(Value & v, int n)
 {
     v.type = tInt;
diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc
index e0ad91d8a5..82a92416be 100644
--- a/src/libexpr/get-drvs.cc
+++ b/src/libexpr/get-drvs.cc
@@ -10,7 +10,7 @@ string DrvInfo::queryDrvPath(EvalState & state) const
     if (drvPath == "" && attrs) {
         Bindings::iterator i = attrs->find(state.sDrvPath);
         PathSet context;
-        (string &) drvPath = i != attrs->end() ? state.coerceToPath(i->second, context) : "";
+        (string &) drvPath = i != attrs->end() ? state.coerceToPath(i->second.value, context) : "";
     }
     return drvPath;
 }
@@ -21,7 +21,7 @@ string DrvInfo::queryOutPath(EvalState & state) const
     if (outPath == "" && attrs) {
         Bindings::iterator i = attrs->find(state.sOutPath);
         PathSet context;
-        (string &) outPath = i != attrs->end() ? state.coerceToPath(i->second, context) : "";
+        (string &) outPath = i != attrs->end() ? state.coerceToPath(i->second.value, context) : "";
     }
     return outPath;
 }
@@ -36,21 +36,21 @@ MetaInfo DrvInfo::queryMetaInfo(EvalState & state) const
     Bindings::iterator a = attrs->find(state.sMeta);
     if (a == attrs->end()) return meta; /* fine, empty meta information */
 
-    state.forceAttrs(a->second);
+    state.forceAttrs(a->second.value);
 
-    foreach (Bindings::iterator, i, *a->second.attrs) {
+    foreach (Bindings::iterator, i, *a->second.value.attrs) {
         MetaValue value;
-        state.forceValue(i->second);
-        if (i->second.type == tString) {
+        state.forceValue(i->second.value);
+        if (i->second.value.type == tString) {
             value.type = MetaValue::tpString;
-            value.stringValue = i->second.string.s;
-        } else if (i->second.type == tInt) {
+            value.stringValue = i->second.value.string.s;
+        } else if (i->second.value.type == tInt) {
             value.type = MetaValue::tpInt;
-            value.intValue = i->second.integer;
-        } else if (i->second.type == tList) {
+            value.intValue = i->second.value.integer;
+        } else if (i->second.value.type == tList) {
             value.type = MetaValue::tpStrings;
-            for (unsigned int j = 0; j < i->second.list.length; ++j)
-                value.stringValues.push_back(state.forceStringNoCtx(*i->second.list.elems[j]));
+            for (unsigned int j = 0; j < i->second.value.list.length; ++j)
+                value.stringValues.push_back(state.forceStringNoCtx(*i->second.value.list.elems[j]));
         } else continue;
         ((MetaInfo &) meta)[i->first] = value;
     }
@@ -99,13 +99,13 @@ static bool getDerivation(EvalState & state, Value & v,
         Bindings::iterator i = v.attrs->find(state.sName);
         /* !!! We really would like to have a decent back trace here. */
         if (i == v.attrs->end()) throw TypeError("derivation name missing");
-        drv.name = state.forceStringNoCtx(i->second);
+        drv.name = state.forceStringNoCtx(i->second.value);
 
         i = v.attrs->find(state.sSystem);
         if (i == v.attrs->end())
             drv.system = "unknown";
         else
-            drv.system = state.forceStringNoCtx(i->second);
+            drv.system = state.forceStringNoCtx(i->second.value);
 
         drv.attrs = v.attrs;
 
@@ -168,7 +168,7 @@ static void getDerivations(EvalState & state, Value & vIn,
         foreach (SortedSymbols::iterator, i, attrs) {
             startNest(nest, lvlDebug, format("evaluating attribute `%1%'") % i->first);
             string pathPrefix2 = addToPath(pathPrefix, i->first);
-            Value & v2((*v.attrs)[i->second]);
+            Value & v2((*v.attrs)[i->second].value);
             if (combineChannels)
                 getDerivations(state, v2, pathPrefix2, autoArgs, drvs, done);
             else if (getDerivation(state, v2, pathPrefix2, drvs, done)) {
@@ -178,7 +178,7 @@ static void getDerivations(EvalState & state, Value & vIn,
                    attribute. */
                 if (v2.type == tAttrs) {
                     Bindings::iterator j = v2.attrs->find(state.symbols.create("recurseForDerivations"));
-                    if (j != v2.attrs->end() && state.forceBool(j->second))
+                    if (j != v2.attrs->end() && state.forceBool(j->second.value))
                         getDerivations(state, v2, pathPrefix2, autoArgs, drvs, done);
                 }
             }
diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc
index 9023d2b1be..981f87ce81 100644
--- a/src/libexpr/primops.cc
+++ b/src/libexpr/primops.cc
@@ -115,18 +115,18 @@ static void prim_genericClosure(EvalState & state, Value * * args, Value & v)
         args[0]->attrs->find(state.symbols.create("startSet"));
     if (startSet == args[0]->attrs->end())
         throw EvalError("attribute `startSet' required");
-    state.forceList(startSet->second);
+    state.forceList(startSet->second.value);
 
     list<Value *> workSet;
-    for (unsigned int n = 0; n < startSet->second.list.length; ++n)
-        workSet.push_back(startSet->second.list.elems[n]);
+    for (unsigned int n = 0; n < startSet->second.value.list.length; ++n)
+        workSet.push_back(startSet->second.value.list.elems[n]);
 
     /* Get the operator. */
     Bindings::iterator op =
         args[0]->attrs->find(state.symbols.create("operator"));
     if (op == args[0]->attrs->end())
         throw EvalError("attribute `operator' required");
-    state.forceValue(op->second);
+    state.forceValue(op->second.value);
 
     /* Construct the closure by applying the operator to element of
        `workSet', adding the result to `workSet', continuing until
@@ -143,15 +143,15 @@ static void prim_genericClosure(EvalState & state, Value * * args, Value & v)
             e->attrs->find(state.symbols.create("key"));
         if (key == e->attrs->end())
             throw EvalError("attribute `key' required");
-        state.forceValue(key->second);
+        state.forceValue(key->second.value);
 
-        if (doneKeys.find(key->second) != doneKeys.end()) continue;
-        doneKeys.insert(key->second);
+        if (doneKeys.find(key->second.value) != doneKeys.end()) continue;
+        doneKeys.insert(key->second.value);
         res.push_back(*e);
         
         /* Call the `operator' function with `e' as argument. */
         Value call;
-        mkApp(call, op->second, *e);
+        mkApp(call, op->second.value, *e);
         state.forceList(call);
 
         /* Add the values returned by the operator to the work set. */
@@ -323,11 +323,11 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v)
     if (attr == args[0]->attrs->end())
         throw EvalError("required attribute `name' missing");
     string drvName;
+    Pos & posDrvName(*attr->second.pos);
     try {        
-        drvName = state.forceStringNoCtx(attr->second);
+        drvName = state.forceStringNoCtx(attr->second.value);
     } catch (Error & e) {
-        e.addPrefix(format("while evaluating the derivation attribute `name' at <SOMEWHERE>:\n"));
-        // !!! % showPos(posDrvName));
+        e.addPrefix(format("while evaluating the derivation attribute `name' at %1%:\n") % posDrvName);
         throw;
     }
     
@@ -348,9 +348,9 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v)
             /* The `args' attribute is special: it supplies the
                command-line arguments to the builder. */
             if (key == "args") {
-                state.forceList(i->second);
-                for (unsigned int n = 0; n < i->second.list.length; ++n) {
-                    string s = state.coerceToString(*i->second.list.elems[n], context, true);
+                state.forceList(i->second.value);
+                for (unsigned int n = 0; n < i->second.value.list.length; ++n) {
+                    string s = state.coerceToString(*i->second.value.list.elems[n], context, true);
                     drv.args.push_back(s);
                 }
             }
@@ -358,7 +358,7 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v)
             /* All other attributes are passed to the builder through
                the environment. */
             else {
-                string s = state.coerceToString(i->second, context, true);
+                string s = state.coerceToString(i->second.value, context, true);
                 drv.env[key] = s;
                 if (key == "builder") drv.builder = s;
                 else if (i->first == state.sSystem) drv.platform = s;
@@ -373,10 +373,10 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v)
             }
 
         } catch (Error & e) {
-            e.addPrefix(format("while evaluating the derivation attribute `%1%' at <SOMEWHERE>:\n")
-                % key /* !!! % showPos(pos) */);
-            e.addPrefix(format("while instantiating the derivation named `%1%' at <SOMEWHERE>:\n")
-                % drvName /* !!! % showPos(posDrvName) */);
+            e.addPrefix(format("while evaluating the derivation attribute `%1%' at %2%:\n")
+                % key % *i->second.pos);
+            e.addPrefix(format("while instantiating the derivation named `%1%' at %2%:\n")
+                % drvName % posDrvName);
             throw;
         }
     }
@@ -487,8 +487,8 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v)
 
     /* !!! assumes a single output */
     state.mkAttrs(v);
-    mkString((*v.attrs)[state.sOutPath], outPath, singleton<PathSet>(drvPath));
-    mkString((*v.attrs)[state.sDrvPath], drvPath, singleton<PathSet>("=" + drvPath));
+    mkString((*v.attrs)[state.sOutPath].value, outPath, singleton<PathSet>(drvPath));
+    mkString((*v.attrs)[state.sDrvPath].value, drvPath, singleton<PathSet>("=" + drvPath));
 }
 
 
@@ -711,8 +711,9 @@ static void prim_getAttr(EvalState & state, Value * * args, Value & v)
     Bindings::iterator i = args[1]->attrs->find(state.symbols.create(attr));
     if (i == args[1]->attrs->end())
         throw EvalError(format("attribute `%1%' missing") % attr);
-    state.forceValue(i->second);
-    v = i->second;
+    // !!! add to stack trace?
+    state.forceValue(i->second.value);
+    v = i->second.value;
 }
 
 
@@ -764,13 +765,15 @@ static void prim_listToAttrs(EvalState & state, Value * * args, Value & v)
         Bindings::iterator j = v2.attrs->find(state.sName);
         if (j == v2.attrs->end())
             throw TypeError("`name' attribute missing in a call to `listToAttrs'");
-        string name = state.forceStringNoCtx(j->second);
+        string name = state.forceStringNoCtx(j->second.value);
         
         j = v2.attrs->find(state.symbols.create("value"));
         if (j == v2.attrs->end())
             throw TypeError("`value' attribute missing in a call to `listToAttrs'");
 
-        mkCopy((*v.attrs)[state.symbols.create(name)], j->second);
+        Attr & a = (*v.attrs)[state.symbols.create(name)];
+        mkCopy(a.value, j->second.value);
+        a.pos = j->second.pos;
     }
 }
 
@@ -787,8 +790,11 @@ static void prim_intersectAttrs(EvalState & state, Value * * args, Value & v)
     
     foreach (Bindings::iterator, i, *args[1]->attrs) {
         Bindings::iterator j = args[0]->attrs->find(i->first);
-        if (j != args[0]->attrs->end())
-            mkCopy((*v.attrs)[i->first], i->second);
+        if (j != args[0]->attrs->end()) {
+            Attr & a = (*v.attrs)[i->first];
+            mkCopy(a.value, i->second.value);
+            a.pos = i->second.pos;
+        }
     }
 }
 
@@ -817,7 +823,7 @@ static void prim_functionArgs(EvalState & state, Value * * args, Value & v)
     if (!args[0]->lambda.fun->matchAttrs) return;
 
     foreach (Formals::Formals_::iterator, i, args[0]->lambda.fun->formals->formals)
-        mkBool((*v.attrs)[i->name], i->def);
+        mkBool((*v.attrs)[i->name].value, i->def);
 }
 
 
@@ -1000,8 +1006,8 @@ static void prim_parseDrvName(EvalState & state, Value * * args, Value & v)
     string name = state.forceStringNoCtx(*args[0]);
     DrvName parsed(name);
     state.mkAttrs(v);
-    mkString((*v.attrs)[state.sName], parsed.name);
-    mkString((*v.attrs)[state.symbols.create("version")], parsed.version);
+    mkString((*v.attrs)[state.sName].value, parsed.name);
+    mkString((*v.attrs)[state.symbols.create("version")].value, parsed.version);
 }
 
 
diff --git a/src/libexpr/value-to-xml.cc b/src/libexpr/value-to-xml.cc
index 58e89925ce..821f715e23 100644
--- a/src/libexpr/value-to-xml.cc
+++ b/src/libexpr/value-to-xml.cc
@@ -28,7 +28,8 @@ static void showAttrs(EvalState & state, bool strict, Bindings & attrs,
         names.insert(i->first);
     foreach (StringSet::iterator, i, names) {
         XMLOpenElement _(doc, "attr", singletonAttrs("name", *i));
-        printValueAsXML(state, strict, attrs[state.symbols.create(*i)], doc, context, drvsSeen);
+        printValueAsXML(state, strict, attrs[state.symbols.create(*i)].value,
+            doc, context, drvsSeen);
     }
 }
 
@@ -71,12 +72,12 @@ static void printValueAsXML(EvalState & state, bool strict, Value & v,
 
                 Path drvPath;
                 a = v.attrs->find(state.sDrvPath);
-                if (a != v.attrs->end() && a->second.type == tString)
-                    xmlAttrs["drvPath"] = drvPath = a->second.string.s;
+                if (a != v.attrs->end() && a->second.value.type == tString)
+                    xmlAttrs["drvPath"] = drvPath = a->second.value.string.s;
         
                 a = v.attrs->find(state.sOutPath);
-                if (a != v.attrs->end() && a->second.type == tString)
-                    xmlAttrs["outPath"] = a->second.string.s;
+                if (a != v.attrs->end() && a->second.value.type == tString)
+                    xmlAttrs["outPath"] = a->second.value.string.s;
 
                 XMLOpenElement _(doc, "derivation", xmlAttrs);