about summary refs log tree commit diff
path: root/src/libexpr
diff options
context:
space:
mode:
Diffstat (limited to 'src/libexpr')
-rw-r--r--src/libexpr/attr-path.cc11
-rw-r--r--src/libexpr/download.cc236
-rw-r--r--src/libexpr/download.hh22
-rw-r--r--src/libexpr/eval-inline.hh4
-rw-r--r--src/libexpr/eval.cc171
-rw-r--r--src/libexpr/get-drvs.cc38
-rw-r--r--src/libexpr/json-to-value.cc2
-rw-r--r--src/libexpr/local.mk2
-rw-r--r--src/libexpr/names.cc4
-rw-r--r--src/libexpr/nixexpr.cc92
-rw-r--r--src/libexpr/parser.y20
-rw-r--r--src/libexpr/primops.cc262
-rw-r--r--src/libexpr/value-to-json.cc34
-rw-r--r--src/libexpr/value-to-xml.cc46
-rw-r--r--src/libexpr/value.hh29
15 files changed, 405 insertions, 568 deletions
diff --git a/src/libexpr/attr-path.cc b/src/libexpr/attr-path.cc
index fdd61a5fd375..55379f94b189 100644
--- a/src/libexpr/attr-path.cc
+++ b/src/libexpr/attr-path.cc
@@ -42,11 +42,10 @@ Value * findAlongAttrPath(EvalState & state, const string & attrPath,
 
     Value * v = &vIn;
 
-    foreach (Strings::iterator, i, tokens) {
+    for (auto & attr : tokens) {
 
-        /* Is *i an index (integer) or a normal attribute name? */
+        /* Is i an index (integer) or a normal attribute name? */
         enum { apAttr, apIndex } apType = apAttr;
-        string attr = *i;
         unsigned int attrIndex;
         if (string2Int(attr, attrIndex)) apType = apIndex;
 
@@ -77,15 +76,15 @@ Value * findAlongAttrPath(EvalState & state, const string & attrPath,
 
         else if (apType == apIndex) {
 
-            if (v->type != tList)
+            if (!v->isList())
                 throw TypeError(
                     format("the expression selected by the selection path ‘%1%’ should be a list but is %2%")
                     % attrPath % showType(*v));
 
-            if (attrIndex >= v->list.length)
+            if (attrIndex >= v->listSize())
                 throw Error(format("list index %1% in selection path ‘%2%’ is out of range") % attrIndex % attrPath);
 
-            v = v->list.elems[attrIndex];
+            v = v->listElems()[attrIndex];
         }
 
     }
diff --git a/src/libexpr/download.cc b/src/libexpr/download.cc
deleted file mode 100644
index 9bf3e13aa9da..000000000000
--- a/src/libexpr/download.cc
+++ /dev/null
@@ -1,236 +0,0 @@
-#include "download.hh"
-#include "util.hh"
-#include "globals.hh"
-#include "hash.hh"
-#include "store-api.hh"
-
-#include <curl/curl.h>
-
-namespace nix {
-
-struct Curl
-{
-    CURL * curl;
-    string data;
-    string etag, status, expectedETag;
-
-    struct curl_slist * requestHeaders;
-
-    static size_t writeCallback(void * contents, size_t size, size_t nmemb, void * userp)
-    {
-        Curl & c(* (Curl *) userp);
-        size_t realSize = size * nmemb;
-        c.data.append((char *) contents, realSize);
-        return realSize;
-    }
-
-    static size_t headerCallback(void * contents, size_t size, size_t nmemb, void * userp)
-    {
-        Curl & c(* (Curl *) userp);
-        size_t realSize = size * nmemb;
-        string line = string((char *) contents, realSize);
-        printMsg(lvlVomit, format("got header: %1%") % trim(line));
-        if (line.compare(0, 5, "HTTP/") == 0) { // new response starts
-            c.etag = "";
-            auto ss = tokenizeString<vector<string>>(line, " ");
-            c.status = ss.size() >= 2 ? ss[1] : "";
-        } else {
-            auto i = line.find(':');
-            if (i != string::npos) {
-                string name = trim(string(line, 0, i));
-                if (name == "ETag") { // FIXME: case
-                    c.etag = trim(string(line, i + 1));
-                    /* Hack to work around a GitHub bug: it sends
-                       ETags, but ignores If-None-Match. So if we get
-                       the expected ETag on a 200 response, then shut
-                       down the connection because we already have the
-                       data. */
-                    printMsg(lvlDebug, format("got ETag: %1%") % c.etag);
-                    if (c.etag == c.expectedETag && c.status == "200") {
-                        printMsg(lvlDebug, format("shutting down on 200 HTTP response with expected ETag"));
-                        return 0;
-                    }
-                }
-            }
-        }
-        return realSize;
-    }
-
-    static int progressCallback(void * clientp, double dltotal, double dlnow, double ultotal, double ulnow)
-    {
-        return _isInterrupted;
-    }
-
-    Curl()
-    {
-        requestHeaders = 0;
-
-        curl = curl_easy_init();
-        if (!curl) throw Error("unable to initialize curl");
-
-        curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
-        curl_easy_setopt(curl, CURLOPT_CAINFO, getEnv("SSL_CERT_FILE", "/etc/ssl/certs/ca-certificates.crt").c_str());
-        curl_easy_setopt(curl, CURLOPT_USERAGENT, ("Nix/" + nixVersion).c_str());
-        curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
-
-        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeCallback);
-        curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *) &curl);
-
-        curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, headerCallback);
-        curl_easy_setopt(curl, CURLOPT_HEADERDATA, (void *) &curl);
-
-        curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, progressCallback);
-        curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
-    }
-
-    ~Curl()
-    {
-        if (curl) curl_easy_cleanup(curl);
-        if (requestHeaders) curl_slist_free_all(requestHeaders);
-    }
-
-    bool fetch(const string & url, const string & expectedETag = "")
-    {
-        curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
-
-        data.clear();
-
-        if (requestHeaders) {
-            curl_slist_free_all(requestHeaders);
-            requestHeaders = 0;
-        }
-
-        if (!expectedETag.empty()) {
-            this->expectedETag = expectedETag;
-            requestHeaders = curl_slist_append(requestHeaders, ("If-None-Match: " + expectedETag).c_str());
-        }
-
-        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, requestHeaders);
-
-        CURLcode res = curl_easy_perform(curl);
-        checkInterrupt();
-        if (res == CURLE_WRITE_ERROR && etag == expectedETag) return false;
-        if (res != CURLE_OK)
-            throw DownloadError(format("unable to download ‘%1%’: %2% (%3%)")
-                % url % curl_easy_strerror(res) % res);
-
-        long httpStatus = 0;
-        curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &httpStatus);
-        if (httpStatus == 304) return false;
-
-        return true;
-    }
-};
-
-
-DownloadResult downloadFile(string url, string expectedETag)
-{
-    DownloadResult res;
-    Curl curl;
-    if (curl.fetch(url, expectedETag)) {
-        res.cached = false;
-        res.data = curl.data;
-    } else
-        res.cached = true;
-    res.etag = curl.etag;
-    return res;
-}
-
-
-Path downloadFileCached(const string & url, bool unpack)
-{
-    Path cacheDir = getEnv("XDG_CACHE_HOME", getEnv("HOME", "") + "/.cache") + "/nix/tarballs";
-    createDirs(cacheDir);
-
-    string urlHash = printHash32(hashString(htSHA256, url));
-
-    Path dataFile = cacheDir + "/" + urlHash + ".info";
-    Path fileLink = cacheDir + "/" + urlHash + "-file";
-
-    Path storePath;
-
-    string expectedETag;
-
-    int ttl = settings.get("tarball-ttl", 60 * 60);
-    bool skip = false;
-
-    if (pathExists(fileLink) && pathExists(dataFile)) {
-        storePath = readLink(fileLink);
-        store->addTempRoot(storePath);
-        if (store->isValidPath(storePath)) {
-            auto ss = tokenizeString<vector<string>>(readFile(dataFile), "\n");
-            if (ss.size() >= 3 && ss[0] == url) {
-                time_t lastChecked;
-                if (string2Int(ss[2], lastChecked) && lastChecked + ttl >= time(0))
-                    skip = true;
-                else if (!ss[1].empty()) {
-                    printMsg(lvlDebug, format("verifying previous ETag ‘%1%’") % ss[1]);
-                    expectedETag = ss[1];
-                }
-            }
-        } else
-            storePath = "";
-    }
-
-    string name;
-    auto p = url.rfind('/');
-    if (p != string::npos) name = string(url, p + 1);
-
-    if (!skip) {
-
-        if (storePath.empty())
-            printMsg(lvlInfo, format("downloading ‘%1%’...") % url);
-        else
-            printMsg(lvlInfo, format("checking ‘%1%’...") % url);
-
-        try {
-            auto res = downloadFile(url, expectedETag);
-
-            if (!res.cached)
-                storePath = store->addTextToStore(name, res.data, PathSet(), false);
-
-            assert(!storePath.empty());
-            replaceSymlink(storePath, fileLink);
-
-            writeFile(dataFile, url + "\n" + res.etag + "\n" + int2String(time(0)) + "\n");
-        } catch (DownloadError & e) {
-            if (storePath.empty()) throw;
-            printMsg(lvlError, format("warning: %1%; using cached result") % e.msg());
-        }
-    }
-
-    if (unpack) {
-        Path unpackedLink = cacheDir + "/" + baseNameOf(storePath) + "-unpacked";
-        Path unpackedStorePath;
-        if (pathExists(unpackedLink)) {
-            unpackedStorePath = readLink(unpackedLink);
-            store->addTempRoot(unpackedStorePath);
-            if (!store->isValidPath(unpackedStorePath))
-                unpackedStorePath = "";
-        }
-        if (unpackedStorePath.empty()) {
-            printMsg(lvlInfo, format("unpacking ‘%1%’...") % url);
-            Path tmpDir = createTempDir();
-            AutoDelete autoDelete(tmpDir, true);
-            // FIXME: this requires GNU tar for decompression.
-            runProgram("tar", true, {"xf", storePath, "-C", tmpDir, "--strip-components", "1"}, "");
-            unpackedStorePath = store->addToStore(name, tmpDir, true, htSHA256, defaultPathFilter, false);
-        }
-        replaceSymlink(unpackedStorePath, unpackedLink);
-        return unpackedStorePath;
-    }
-
-    return storePath;
-}
-
-
-bool isUri(const string & s)
-{
-    size_t pos = s.find("://");
-    if (pos == string::npos) return false;
-    string scheme(s, 0, pos);
-    return scheme == "http" || scheme == "https" || scheme == "file";
-}
-
-
-}
diff --git a/src/libexpr/download.hh b/src/libexpr/download.hh
deleted file mode 100644
index 28c9117e4227..000000000000
--- a/src/libexpr/download.hh
+++ /dev/null
@@ -1,22 +0,0 @@
-#pragma once
-
-#include "types.hh"
-#include <string>
-
-namespace nix {
-
-struct DownloadResult
-{
-    bool cached;
-    string data, etag;
-};
-
-DownloadResult downloadFile(string url, string expectedETag = "");
-
-Path downloadFileCached(const string & url, bool unpack);
-
-MakeError(DownloadError, Error)
-
-bool isUri(const string & s);
-
-}
diff --git a/src/libexpr/eval-inline.hh b/src/libexpr/eval-inline.hh
index c275f7ba83e8..178e06c80186 100644
--- a/src/libexpr/eval-inline.hh
+++ b/src/libexpr/eval-inline.hh
@@ -66,7 +66,7 @@ inline void EvalState::forceAttrs(Value & v, const Pos & pos)
 inline void EvalState::forceList(Value & v)
 {
     forceValue(v);
-    if (v.type != tList)
+    if (!v.isList())
         throwTypeError("value is %1% while a list was expected", v);
 }
 
@@ -74,7 +74,7 @@ inline void EvalState::forceList(Value & v)
 inline void EvalState::forceList(Value & v, const Pos & pos)
 {
     forceValue(v);
-    if (v.type != tList)
+    if (!v.isList())
         throwTypeError("value is %1% while a list was expected, at %2%", v, pos);
 }
 
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index eab38c9da174..044256112d50 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -57,6 +57,8 @@ static void * allocBytes(size_t n)
 
 static void printValue(std::ostream & str, std::set<const Value *> & active, const Value & v)
 {
+    checkInterrupt();
+
     if (active.find(&v) != active.end()) {
         str << "<CYCLE>";
         return;
@@ -90,8 +92,8 @@ static void printValue(std::ostream & str, std::set<const Value *> & active, con
         str << "{ ";
         typedef std::map<string, Value *> Sorted;
         Sorted sorted;
-        foreach (Bindings::iterator, i, *v.attrs)
-            sorted[i->name] = i->value;
+        for (auto & i : *v.attrs)
+            sorted[i.name] = i.value;
         for (auto & i : sorted) {
             str << i.first << " = ";
             printValue(str, active, *i.second);
@@ -100,10 +102,12 @@ static void printValue(std::ostream & str, std::set<const Value *> & active, con
         str << "}";
         break;
     }
-    case tList:
+    case tList1:
+    case tList2:
+    case tListN:
         str << "[ ";
-        for (unsigned int n = 0; n < v.list.length; ++n) {
-            printValue(str, active, *v.list.elems[n]);
+        for (unsigned int n = 0; n < v.listSize(); ++n) {
+            printValue(str, active, *v.listElems()[n]);
             str << " ";
         }
         str << "]";
@@ -149,7 +153,7 @@ string showType(const Value & v)
         case tPath: return "a path";
         case tNull: return "null";
         case tAttrs: return "a set";
-        case tList: return "a list";
+        case tList1: case tList2: case tListN: return "a list";
         case tThunk: return "a thunk";
         case tApp: return "a function application";
         case tLambda: return "a function";
@@ -434,8 +438,8 @@ void mkString(Value & v, const string & s, const PathSet & context)
         unsigned int n = 0;
         v.string.context = (const char * *)
             allocBytes((context.size() + 1) * sizeof(char *));
-        foreach (PathSet::const_iterator, i, context)
-            v.string.context[n++] = dupString(i->c_str());
+        for (auto & i : context)
+            v.string.context[n++] = dupString(i.c_str());
         v.string.context[n] = 0;
     }
 }
@@ -497,13 +501,19 @@ Env & EvalState::allocEnv(unsigned int size)
 }
 
 
-void EvalState::mkList(Value & v, unsigned int length)
+void EvalState::mkList(Value & v, unsigned int size)
 {
     clearValue(v);
-    v.type = tList;
-    v.list.length = length;
-    v.list.elems = length ? (Value * *) allocBytes(length * sizeof(Value *)) : 0;
-    nrListElems += length;
+    if (size == 1)
+        v.type = tList1;
+    else if (size == 2)
+        v.type = tList2;
+    else {
+        v.type = tListN;
+        v.bigList.size = size;
+        v.bigList.elems = size ? (Value * *) allocBytes(size * sizeof(Value *)) : 0;
+    }
+    nrListElems += size;
 }
 
 
@@ -691,15 +701,15 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
            environment, while the inherited attributes are evaluated
            in the original environment. */
         unsigned int displ = 0;
-        foreach (AttrDefs::iterator, i, attrs) {
+        for (auto & i : attrs) {
             Value * vAttr;
-            if (hasOverrides && !i->second.inherited) {
+            if (hasOverrides && !i.second.inherited) {
                 vAttr = state.allocValue();
-                mkThunk(*vAttr, env2, i->second.e);
+                mkThunk(*vAttr, env2, i.second.e);
             } else
-                vAttr = i->second.e->maybeThunk(state, i->second.inherited ? env : env2);
+                vAttr = i.second.e->maybeThunk(state, i.second.inherited ? env : env2);
             env2.values[displ++] = vAttr;
-            v.attrs->push_back(Attr(i->first, vAttr, &i->second.pos));
+            v.attrs->push_back(Attr(i.first, vAttr, &i.second.pos));
         }
 
         /* If the rec contains an attribute called `__overrides', then
@@ -730,13 +740,13 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
     }
 
     else
-        foreach (AttrDefs::iterator, i, attrs)
-            v.attrs->push_back(Attr(i->first, i->second.e->maybeThunk(state, env), &i->second.pos));
+        for (auto & i : attrs)
+            v.attrs->push_back(Attr(i.first, i.second.e->maybeThunk(state, env), &i.second.pos));
 
     /* Dynamic attrs apply *after* rec and __overrides. */
-    foreach (DynamicAttrDefs::iterator, i, dynamicAttrs) {
+    for (auto & i : dynamicAttrs) {
         Value nameVal;
-        i->nameExpr->eval(state, *dynamicEnv, nameVal);
+        i.nameExpr->eval(state, *dynamicEnv, nameVal);
         state.forceValue(nameVal);
         if (nameVal.type == tNull)
             continue;
@@ -744,11 +754,11 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
         Symbol nameSym = state.symbols.create(nameVal.string.s);
         Bindings::iterator j = v.attrs->find(nameSym);
         if (j != v.attrs->end())
-            throwEvalError("dynamic attribute ‘%1%’ at %2% already defined at %3%", nameSym, i->pos, *j->pos);
+            throwEvalError("dynamic attribute ‘%1%’ at %2% already defined at %3%", nameSym, i.pos, *j->pos);
 
-        i->valueExpr->setName(nameSym);
+        i.valueExpr->setName(nameSym);
         /* Keep sorted order so find can catch duplicates */
-        v.attrs->push_back(Attr(nameSym, i->valueExpr->maybeThunk(state, *dynamicEnv), &i->pos));
+        v.attrs->push_back(Attr(nameSym, i.valueExpr->maybeThunk(state, *dynamicEnv), &i.pos));
         v.attrs->sort(); // FIXME: inefficient
     }
 }
@@ -765,8 +775,8 @@ void ExprLet::eval(EvalState & state, Env & env, Value & v)
        while the inherited attributes are evaluated in the original
        environment. */
     unsigned int displ = 0;
-    foreach (ExprAttrs::AttrDefs::iterator, i, attrs->attrs)
-        env2.values[displ++] = i->second.e->maybeThunk(state, i->second.inherited ? env : env2);
+    for (auto & i : attrs->attrs)
+        env2.values[displ++] = i.second.e->maybeThunk(state, i.second.inherited ? env : env2);
 
     body->eval(state, env2, v);
 }
@@ -775,8 +785,8 @@ void ExprLet::eval(EvalState & state, Env & env, Value & v)
 void ExprList::eval(EvalState & state, Env & env, Value & v)
 {
     state.mkList(v, elems.size());
-    for (unsigned int n = 0; n < v.list.length; ++n)
-        v.list.elems[n] = elems[n]->maybeThunk(state, env);
+    for (unsigned int n = 0; n < elems.size(); ++n)
+        v.listElems()[n] = elems[n]->maybeThunk(state, env);
 }
 
 
@@ -817,10 +827,10 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
 
     try {
 
-        foreach (AttrPath::const_iterator, i, attrPath) {
+        for (auto & i : attrPath) {
             nrLookups++;
             Bindings::iterator j;
-            Symbol name = getName(*i, state, env);
+            Symbol name = getName(i, state, env);
             if (def) {
                 state.forceValue(*vAttrs);
                 if (vAttrs->type != tAttrs ||
@@ -859,10 +869,10 @@ void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v)
 
     e->eval(state, env, vTmp);
 
-    foreach (AttrPath::const_iterator, i, attrPath) {
+    for (auto & i : attrPath) {
         state.forceValue(*vAttrs);
         Bindings::iterator j;
-        Symbol name = getName(*i, state, env);
+        Symbol name = getName(i, state, env);
         if (vAttrs->type != tAttrs ||
             (j = vAttrs->attrs->find(name)) == vAttrs->attrs->end())
         {
@@ -975,12 +985,12 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v, const Pos & po
            there is no matching actual argument but the formal
            argument has a default, use the default. */
         unsigned int attrsUsed = 0;
-        foreach (Formals::Formals_::iterator, i, lambda.formals->formals) {
-            Bindings::iterator j = arg.attrs->find(i->name);
+        for (auto & i : lambda.formals->formals) {
+            Bindings::iterator j = arg.attrs->find(i.name);
             if (j == arg.attrs->end()) {
-                if (!i->def) throwTypeError("%1% called without required argument ‘%2%’, at %3%",
-                    lambda, i->name, pos);
-                env2.values[displ++] = i->def->maybeThunk(*this, env2);
+                if (!i.def) throwTypeError("%1% called without required argument ‘%2%’, at %3%",
+                    lambda, i.name, pos);
+                env2.values[displ++] = i.def->maybeThunk(*this, env2);
             } else {
                 attrsUsed++;
                 env2.values[displ++] = j->value;
@@ -992,9 +1002,9 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v, const Pos & po
         if (!lambda.formals->ellipsis && attrsUsed != arg.attrs->size()) {
             /* Nope, so show the first unexpected argument to the
                user. */
-            foreach (Bindings::iterator, i, *arg.attrs)
-                if (lambda.formals->argNames.find(i->name) == lambda.formals->argNames.end())
-                    throwTypeError("%1% called with unexpected argument ‘%2%’, at %3%", lambda, i->name, pos);
+            for (auto & i : *arg.attrs)
+                if (lambda.formals->argNames.find(i.name) == lambda.formals->argNames.end())
+                    throwTypeError("%1% called with unexpected argument ‘%2%’, at %3%", lambda, i.name, pos);
             abort(); // can't happen
         }
     }
@@ -1036,12 +1046,12 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res)
     Value * actualArgs = allocValue();
     mkAttrs(*actualArgs, fun.lambda.fun->formals->formals.size());
 
-    foreach (Formals::Formals_::iterator, i, fun.lambda.fun->formals->formals) {
-        Bindings::iterator j = args.find(i->name);
+    for (auto & i : fun.lambda.fun->formals->formals) {
+        Bindings::iterator j = args.find(i.name);
         if (j != args.end())
             actualArgs->attrs->push_back(*j);
-        else if (!i->def)
-            throwTypeError("cannot auto-call a function that has an argument without a default value (‘%1%’)", i->name);
+        else if (!i.def)
+            throwTypeError("cannot auto-call a function that has an argument without a default value (‘%1%’)", i.name);
     }
 
     actualArgs->attrs->sort();
@@ -1169,20 +1179,21 @@ void EvalState::concatLists(Value & v, unsigned int nrLists, Value * * lists, co
     unsigned int len = 0;
     for (unsigned int n = 0; n < nrLists; ++n) {
         forceList(*lists[n], pos);
-        unsigned int l = lists[n]->list.length;
+        unsigned int l = lists[n]->listSize();
         len += l;
         if (l) nonEmpty = lists[n];
     }
 
-    if (nonEmpty && len == nonEmpty->list.length) {
+    if (nonEmpty && len == nonEmpty->listSize()) {
         v = *nonEmpty;
         return;
     }
 
     mkList(v, len);
+    auto out = v.listElems();
     for (unsigned int n = 0, pos = 0; n < nrLists; ++n) {
-        unsigned int l = lists[n]->list.length;
-        memcpy(v.list.elems + pos, lists[n]->list.elems, l * sizeof(Value *));
+        unsigned int l = lists[n]->listSize();
+        memcpy(out + pos, lists[n]->listElems(), l * sizeof(Value *));
         pos += l;
     }
 }
@@ -1197,9 +1208,9 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
     bool first = !forceString;
     ValueType firstType = tString;
 
-    foreach (vector<Expr *>::iterator, i, *es) {
+    for (auto & i : *es) {
         Value vTmp;
-        (*i)->eval(state, env, vTmp);
+        i->eval(state, env, vTmp);
 
         /* If the first element is a path, then the result will also
            be a path, we don't copy anything (yet - that's done later,
@@ -1258,9 +1269,9 @@ void EvalState::forceValueDeep(Value & v)
                 }
         }
 
-        else if (v.type == tList) {
-            for (unsigned int n = 0; n < v.list.length; ++n)
-                recurse(*v.list.elems[n]);
+        else if (v.isList()) {
+            for (unsigned int n = 0; n < v.listSize(); ++n)
+                recurse(*v.listElems()[n]);
         }
     };
 
@@ -1384,14 +1395,14 @@ string EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context,
         if (v.type == tInt) return int2String(v.integer);
         if (v.type == tNull) return "";
 
-        if (v.type == tList) {
+        if (v.isList()) {
             string result;
-            for (unsigned int n = 0; n < v.list.length; ++n) {
-                result += coerceToString(pos, *v.list.elems[n],
+            for (unsigned int n = 0; n < v.listSize(); ++n) {
+                result += coerceToString(pos, *v.listElems()[n],
                     context, coerceMore, copyToStore);
-                if (n < v.list.length - 1
+                if (n < v.listSize() - 1
                     /* !!! not quite correct */
-                    && (v.list.elems[n]->type != tList || v.list.elems[n]->list.length != 0))
+                    && (!v.listElems()[n]->isList() || v.listElems()[n]->listSize() != 0))
                     result += " ";
             }
             return result;
@@ -1462,10 +1473,12 @@ bool EvalState::eqValues(Value & v1, Value & v2)
         case tNull:
             return true;
 
-        case tList:
-            if (v1.list.length != v2.list.length) return false;
-            for (unsigned int n = 0; n < v1.list.length; ++n)
-                if (!eqValues(*v1.list.elems[n], *v2.list.elems[n])) return false;
+        case tList1:
+        case tList2:
+        case tListN:
+            if (v1.listSize() != v2.listSize()) return false;
+            for (unsigned int n = 0; n < v1.listSize(); ++n)
+                if (!eqValues(*v1.listElems()[n], *v2.listElems()[n])) return false;
             return true;
 
         case tAttrs: {
@@ -1551,25 +1564,25 @@ void EvalState::printStats()
         printMsg(v, format("calls to %1% primops:") % primOpCalls.size());
         typedef std::multimap<unsigned int, Symbol> PrimOpCalls_;
         PrimOpCalls_ primOpCalls_;
-        foreach (PrimOpCalls::iterator, i, primOpCalls)
-            primOpCalls_.insert(std::pair<unsigned int, Symbol>(i->second, i->first));
-        foreach_reverse (PrimOpCalls_::reverse_iterator, i, primOpCalls_)
+        for (auto & i : primOpCalls)
+            primOpCalls_.insert(std::pair<unsigned int, Symbol>(i.second, i.first));
+        for (auto i = primOpCalls_.rbegin(); i != primOpCalls_.rend(); ++i)
             printMsg(v, format("%1$10d %2%") % i->first % i->second);
 
         printMsg(v, format("calls to %1% functions:") % functionCalls.size());
         typedef std::multimap<unsigned int, ExprLambda *> FunctionCalls_;
         FunctionCalls_ functionCalls_;
-        foreach (FunctionCalls::iterator, i, functionCalls)
-            functionCalls_.insert(std::pair<unsigned int, ExprLambda *>(i->second, i->first));
-        foreach_reverse (FunctionCalls_::reverse_iterator, i, functionCalls_)
+        for (auto & i : functionCalls)
+            functionCalls_.insert(std::pair<unsigned int, ExprLambda *>(i.second, i.first));
+        for (auto i = functionCalls_.rbegin(); i != functionCalls_.rend(); ++i)
             printMsg(v, format("%1$10d %2%") % i->first % i->second->showNamePos());
 
         printMsg(v, format("evaluations of %1% attributes:") % attrSelects.size());
         typedef std::multimap<unsigned int, Pos> AttrSelects_;
         AttrSelects_ attrSelects_;
-        foreach (AttrSelects::iterator, i, attrSelects)
-            attrSelects_.insert(std::pair<unsigned int, Pos>(i->second, i->first));
-        foreach_reverse (AttrSelects_::reverse_iterator, i, attrSelects_)
+        for (auto & i : attrSelects)
+            attrSelects_.insert(std::pair<unsigned int, Pos>(i.second, i.first));
+        for (auto i = attrSelects_.rbegin(); i != attrSelects_.rend(); ++i)
             printMsg(v, format("%1$10d %2%") % i->first % i->second);
 
     }
@@ -1613,12 +1626,14 @@ size_t valueSize(Value & v)
                     sz += doValue(*i.value);
             }
             break;
-        case tList:
-            if (seen.find(v.list.elems) == seen.end()) {
-                seen.insert(v.list.elems);
-                sz += v.list.length * sizeof(Value *);
-                for (unsigned int n = 0; n < v.list.length; ++n)
-                    sz += doValue(*v.list.elems[n]);
+        case tList1:
+        case tList2:
+        case tListN:
+            if (seen.find(v.listElems()) == seen.end()) {
+                seen.insert(v.listElems());
+                sz += v.listSize() * sizeof(Value *);
+                for (unsigned int n = 0; n < v.listSize(); ++n)
+                    sz += doValue(*v.listElems()[n]);
             }
             break;
         case tThunk:
diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc
index 1c9fa02a366a..1002ee6285af 100644
--- a/src/libexpr/get-drvs.cc
+++ b/src/libexpr/get-drvs.cc
@@ -39,9 +39,9 @@ DrvInfo::Outputs DrvInfo::queryOutputs()
             state->forceList(*i->value, *i->pos);
 
             /* For each output... */
-            for (unsigned int j = 0; j < i->value->list.length; ++j) {
+            for (unsigned int j = 0; j < i->value->listSize(); ++j) {
                 /* Evaluate the corresponding set. */
-                string name = state->forceStringNoCtx(*i->value->list.elems[j], *i->pos);
+                string name = state->forceStringNoCtx(*i->value->listElems()[j], *i->pos);
                 Bindings::iterator out = attrs->find(state->symbols.create(name));
                 if (out == attrs->end()) continue; // FIXME: throw error?
                 state->forceAttrs(*out->value);
@@ -85,8 +85,8 @@ StringSet DrvInfo::queryMetaNames()
 {
     StringSet res;
     if (!getMeta()) return res;
-    foreach (Bindings::iterator, i, *meta)
-        res.insert(i->name);
+    for (auto & i : *meta)
+        res.insert(i.name);
     return res;
 }
 
@@ -94,16 +94,16 @@ StringSet DrvInfo::queryMetaNames()
 bool DrvInfo::checkMeta(Value & v)
 {
     state->forceValue(v);
-    if (v.type == tList) {
-        for (unsigned int n = 0; n < v.list.length; ++n)
-            if (!checkMeta(*v.list.elems[n])) return false;
+    if (v.isList()) {
+        for (unsigned int n = 0; n < v.listSize(); ++n)
+            if (!checkMeta(*v.listElems()[n])) return false;
         return true;
     }
     else if (v.type == tAttrs) {
         Bindings::iterator i = v.attrs->find(state->sOutPath);
         if (i != v.attrs->end()) return false;
-        foreach (Bindings::iterator, i, *v.attrs)
-            if (!checkMeta(*i->value)) return false;
+        for (auto & i : *v.attrs)
+            if (!checkMeta(*i.value)) return false;
         return true;
     }
     else return v.type == tInt || v.type == tBool || v.type == tString;
@@ -255,13 +255,13 @@ static void getDerivations(EvalState & state, Value & vIn,
            precedence). */
         typedef std::map<string, Symbol> SortedSymbols;
         SortedSymbols attrs;
-        foreach (Bindings::iterator, i, *v.attrs)
-            attrs.insert(std::pair<string, Symbol>(i->name, i->name));
+        for (auto & i : *v.attrs)
+            attrs.insert(std::pair<string, Symbol>(i.name, i.name));
 
-        foreach (SortedSymbols::iterator, i, attrs) {
-            startNest(nest, lvlDebug, format("evaluating attribute ‘%1%’") % i->first);
-            string pathPrefix2 = addToPath(pathPrefix, i->first);
-            Value & v2(*v.attrs->find(i->second)->value);
+        for (auto & i : attrs) {
+            startNest(nest, lvlDebug, format("evaluating attribute ‘%1%’") % i.first);
+            string pathPrefix2 = addToPath(pathPrefix, i.first);
+            Value & v2(*v.attrs->find(i.second)->value);
             if (combineChannels)
                 getDerivations(state, v2, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
             else if (getDerivation(state, v2, pathPrefix2, drvs, done, ignoreAssertionFailures)) {
@@ -277,13 +277,13 @@ static void getDerivations(EvalState & state, Value & vIn,
         }
     }
 
-    else if (v.type == tList) {
-        for (unsigned int n = 0; n < v.list.length; ++n) {
+    else if (v.isList()) {
+        for (unsigned int n = 0; n < v.listSize(); ++n) {
             startNest(nest, lvlDebug,
                 format("evaluating list element"));
             string pathPrefix2 = addToPath(pathPrefix, (format("%1%") % n).str());
-            if (getDerivation(state, *v.list.elems[n], pathPrefix2, drvs, done, ignoreAssertionFailures))
-                getDerivations(state, *v.list.elems[n], pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
+            if (getDerivation(state, *v.listElems()[n], pathPrefix2, drvs, done, ignoreAssertionFailures))
+                getDerivations(state, *v.listElems()[n], pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
         }
     }
 
diff --git a/src/libexpr/json-to-value.cc b/src/libexpr/json-to-value.cc
index 1892b0bac1af..0898b560911b 100644
--- a/src/libexpr/json-to-value.cc
+++ b/src/libexpr/json-to-value.cc
@@ -73,7 +73,7 @@ static void parseJSON(EvalState & state, const char * & s, Value & v)
         s++;
         state.mkList(v, values.size());
         for (size_t n = 0; n < values.size(); ++n)
-            v.list.elems[n] = values[n];
+            v.listElems()[n] = values[n];
     }
 
     else if (*s == '{') {
diff --git a/src/libexpr/local.mk b/src/libexpr/local.mk
index 35e84980a6dd..4c1f4de19187 100644
--- a/src/libexpr/local.mk
+++ b/src/libexpr/local.mk
@@ -8,7 +8,7 @@ libexpr_SOURCES := $(wildcard $(d)/*.cc) $(d)/lexer-tab.cc $(d)/parser-tab.cc
 
 libexpr_LIBS = libutil libstore libformat
 
-libexpr_LDFLAGS = -ldl -lcurl
+libexpr_LDFLAGS = -ldl
 
 # The dependency on libgc must be propagated (i.e. meaning that
 # programs/libraries that use libexpr must explicitly pass -lgc),
diff --git a/src/libexpr/names.cc b/src/libexpr/names.cc
index cda5aa1952ea..7bca9b6550be 100644
--- a/src/libexpr/names.cc
+++ b/src/libexpr/names.cc
@@ -98,8 +98,8 @@ int compareVersions(const string & v1, const string & v2)
 DrvNames drvNamesFromArgs(const Strings & opArgs)
 {
     DrvNames result;
-    foreach (Strings::const_iterator, i, opArgs)
-        result.push_back(DrvName(*i));
+    for (auto & i : opArgs)
+        result.push_back(DrvName(i));
     return result;
 }
 
diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc
index 43f3161f8baf..35db52a13acc 100644
--- a/src/libexpr/nixexpr.cc
+++ b/src/libexpr/nixexpr.cc
@@ -97,21 +97,21 @@ void ExprAttrs::show(std::ostream & str)
 {
     if (recursive) str << "rec ";
     str << "{ ";
-    foreach (AttrDefs::iterator, i, attrs)
-        if (i->second.inherited)
-            str << "inherit " << i->first << " " << "; ";
+    for (auto & i : attrs)
+        if (i.second.inherited)
+            str << "inherit " << i.first << " " << "; ";
         else
-            str << i->first << " = " << *i->second.e << "; ";
-    foreach (DynamicAttrDefs::iterator, i, dynamicAttrs)
-        str << "\"${" << *i->nameExpr << "}\" = " << *i->valueExpr << "; ";
+            str << i.first << " = " << *i.second.e << "; ";
+    for (auto & i : dynamicAttrs)
+        str << "\"${" << *i.nameExpr << "}\" = " << *i.valueExpr << "; ";
     str << "}";
 }
 
 void ExprList::show(std::ostream & str)
 {
     str << "[ ";
-    foreach (vector<Expr *>::iterator, i, elems)
-        str << "(" << **i << ") ";
+    for (auto & i : elems)
+        str << "(" << *i << ") ";
     str << "]";
 }
 
@@ -121,10 +121,10 @@ void ExprLambda::show(std::ostream & str)
     if (matchAttrs) {
         str << "{ ";
         bool first = true;
-        foreach (Formals::Formals_::iterator, i, formals->formals) {
+        for (auto & i : formals->formals) {
             if (first) first = false; else str << ", ";
-            str << i->name;
-            if (i->def) str << " ? " << *i->def;
+            str << i.name;
+            if (i.def) str << " ? " << *i.def;
         }
         if (formals->ellipsis) {
             if (!first) str << ", ";
@@ -140,12 +140,12 @@ void ExprLambda::show(std::ostream & str)
 void ExprLet::show(std::ostream & str)
 {
     str << "(let ";
-    foreach (ExprAttrs::AttrDefs::iterator, i, attrs->attrs)
-        if (i->second.inherited) {
-            str << "inherit " << i->first << "; ";
+    for (auto & i : attrs->attrs)
+        if (i.second.inherited) {
+            str << "inherit " << i.first << "; ";
         }
         else
-            str << i->first << " = " << *i->second.e << "; ";
+            str << i.first << " = " << *i.second.e << "; ";
     str << "in " << *body << ")";
 }
 
@@ -173,9 +173,9 @@ void ExprConcatStrings::show(std::ostream & str)
 {
     bool first = true;
     str << "(";
-    foreach (vector<Expr *>::iterator, i, *es) {
+    for (auto & i : *es) {
         if (first) first = false; else str << " + ";
-        str << **i;
+        str << *i;
     }
     str << ")";
 }
@@ -267,17 +267,17 @@ void ExprSelect::bindVars(const StaticEnv & env)
 {
     e->bindVars(env);
     if (def) def->bindVars(env);
-    foreach (AttrPath::iterator, i, attrPath)
-        if (!i->symbol.set())
-            i->expr->bindVars(env);
+    for (auto & i : attrPath)
+        if (!i.symbol.set())
+            i.expr->bindVars(env);
 }
 
 void ExprOpHasAttr::bindVars(const StaticEnv & env)
 {
     e->bindVars(env);
-    foreach (AttrPath::iterator, i, attrPath)
-        if (!i->symbol.set())
-            i->expr->bindVars(env);
+    for (auto & i : attrPath)
+        if (!i.symbol.set())
+            i.expr->bindVars(env);
 }
 
 void ExprAttrs::bindVars(const StaticEnv & env)
@@ -289,27 +289,27 @@ void ExprAttrs::bindVars(const StaticEnv & env)
         dynamicEnv = &newEnv;
 
         unsigned int displ = 0;
-        foreach (AttrDefs::iterator, i, attrs)
-            newEnv.vars[i->first] = i->second.displ = displ++;
+        for (auto & i : attrs)
+            newEnv.vars[i.first] = i.second.displ = displ++;
 
-        foreach (AttrDefs::iterator, i, attrs)
-            i->second.e->bindVars(i->second.inherited ? env : newEnv);
+        for (auto & i : attrs)
+            i.second.e->bindVars(i.second.inherited ? env : newEnv);
     }
 
     else
-        foreach (AttrDefs::iterator, i, attrs)
-            i->second.e->bindVars(env);
+        for (auto & i : attrs)
+            i.second.e->bindVars(env);
 
-    foreach (DynamicAttrDefs::iterator, i, dynamicAttrs) {
-        i->nameExpr->bindVars(*dynamicEnv);
-        i->valueExpr->bindVars(*dynamicEnv);
+    for (auto & i : dynamicAttrs) {
+        i.nameExpr->bindVars(*dynamicEnv);
+        i.valueExpr->bindVars(*dynamicEnv);
     }
 }
 
 void ExprList::bindVars(const StaticEnv & env)
 {
-    foreach (vector<Expr *>::iterator, i, elems)
-        (*i)->bindVars(env);
+    for (auto & i : elems)
+        i->bindVars(env);
 }
 
 void ExprLambda::bindVars(const StaticEnv & env)
@@ -321,11 +321,11 @@ void ExprLambda::bindVars(const StaticEnv & env)
     if (!arg.empty()) newEnv.vars[arg] = displ++;
 
     if (matchAttrs) {
-        foreach (Formals::Formals_::iterator, i, formals->formals)
-            newEnv.vars[i->name] = displ++;
+        for (auto & i : formals->formals)
+            newEnv.vars[i.name] = displ++;
 
-        foreach (Formals::Formals_::iterator, i, formals->formals)
-            if (i->def) i->def->bindVars(newEnv);
+        for (auto & i : formals->formals)
+            if (i.def) i.def->bindVars(newEnv);
     }
 
     body->bindVars(newEnv);
@@ -336,11 +336,11 @@ void ExprLet::bindVars(const StaticEnv & env)
     StaticEnv newEnv(false, &env);
 
     unsigned int displ = 0;
-    foreach (ExprAttrs::AttrDefs::iterator, i, attrs->attrs)
-        newEnv.vars[i->first] = i->second.displ = displ++;
+    for (auto & i : attrs->attrs)
+        newEnv.vars[i.first] = i.second.displ = displ++;
 
-    foreach (ExprAttrs::AttrDefs::iterator, i, attrs->attrs)
-        i->second.e->bindVars(i->second.inherited ? env : newEnv);
+    for (auto & i : attrs->attrs)
+        i.second.e->bindVars(i.second.inherited ? env : newEnv);
 
     body->bindVars(newEnv);
 }
@@ -384,8 +384,8 @@ void ExprOpNot::bindVars(const StaticEnv & env)
 
 void ExprConcatStrings::bindVars(const StaticEnv & env)
 {
-    foreach (vector<Expr *>::iterator, i, *es)
-        (*i)->bindVars(env);
+    for (auto & i : *es)
+        i->bindVars(env);
 }
 
 void ExprPos::bindVars(const StaticEnv & env)
@@ -419,8 +419,8 @@ string ExprLambda::showNamePos() const
 size_t SymbolTable::totalSize() const
 {
     size_t n = 0;
-    foreach (Symbols::const_iterator, i, symbols)
-        n += i->size();
+    for (auto & i : symbols)
+        n += i.size();
     return n;
 }
 
diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y
index 1f830b7e3c15..d34882f361cf 100644
--- a/src/libexpr/parser.y
+++ b/src/libexpr/parser.y
@@ -136,8 +136,8 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector<Ex
     bool atStartOfLine = true; /* = seen only whitespace in the current line */
     unsigned int minIndent = 1000000;
     unsigned int curIndent = 0;
-    foreach (vector<Expr *>::iterator, i, es) {
-        ExprIndStr * e = dynamic_cast<ExprIndStr *>(*i);
+    for (auto & i : es) {
+        ExprIndStr * e = dynamic_cast<ExprIndStr *>(i);
         if (!e) {
             /* Anti-quotations end the current start-of-line whitespace. */
             if (atStartOfLine) {
@@ -419,20 +419,20 @@ binds
   : binds attrpath '=' expr ';' { $$ = $1; addAttr($$, *$2, $4, makeCurPos(@2, data)); }
   | binds INHERIT attrs ';'
     { $$ = $1;
-      foreach (AttrPath::iterator, i, *$3) {
-          if ($$->attrs.find(i->symbol) != $$->attrs.end())
-              dupAttr(i->symbol, makeCurPos(@3, data), $$->attrs[i->symbol].pos);
+      for (auto & i : *$3) {
+          if ($$->attrs.find(i.symbol) != $$->attrs.end())
+              dupAttr(i.symbol, makeCurPos(@3, data), $$->attrs[i.symbol].pos);
           Pos pos = makeCurPos(@3, data);
-          $$->attrs[i->symbol] = ExprAttrs::AttrDef(new ExprVar(CUR_POS, i->symbol), pos, true);
+          $$->attrs[i.symbol] = ExprAttrs::AttrDef(new ExprVar(CUR_POS, i.symbol), pos, true);
       }
     }
   | binds INHERIT '(' expr ')' attrs ';'
     { $$ = $1;
       /* !!! Should ensure sharing of the expression in $4. */
-      foreach (AttrPath::iterator, i, *$6) {
-          if ($$->attrs.find(i->symbol) != $$->attrs.end())
-              dupAttr(i->symbol, makeCurPos(@6, data), $$->attrs[i->symbol].pos);
-          $$->attrs[i->symbol] = ExprAttrs::AttrDef(new ExprSelect(CUR_POS, $4, i->symbol), makeCurPos(@6, data));
+      for (auto & i : *$6) {
+          if ($$->attrs.find(i.symbol) != $$->attrs.end())
+              dupAttr(i.symbol, makeCurPos(@6, data), $$->attrs[i.symbol].pos);
+          $$->attrs[i.symbol] = ExprAttrs::AttrDef(new ExprSelect(CUR_POS, $4, i.symbol), makeCurPos(@6, data));
       }
     }
   | { $$ = new ExprAttrs; }
diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc
index 355b81adf76d..7a4aad3cd57b 100644
--- a/src/libexpr/primops.cc
+++ b/src/libexpr/primops.cc
@@ -86,9 +86,11 @@ static void prim_scopedImport(EvalState & state, const Pos & pos, Value * * args
     if (isStorePath(path) && store->isValidPath(path) && isDerivation(path)) {
         Derivation drv = readDerivation(path);
         Value & w = *state.allocValue();
-        state.mkAttrs(w, 2 + drv.outputs.size());
+        state.mkAttrs(w, 3 + drv.outputs.size());
         Value * v2 = state.allocAttr(w, state.sDrvPath);
         mkString(*v2, path, singleton<PathSet>("=" + path));
+        v2 = state.allocAttr(w, state.sName);
+        mkString(*v2, drv.env["name"]);
         Value * outputsVal =
             state.allocAttr(w, state.symbols.create("outputs"));
         state.mkList(*outputsVal, drv.outputs.size());
@@ -98,8 +100,8 @@ static void prim_scopedImport(EvalState & state, const Pos & pos, Value * * args
             v2 = state.allocAttr(w, state.symbols.create(o.first));
             mkString(*v2, o.second.path,
                 singleton<PathSet>("!" + o.first + "!" + path));
-            outputsVal->list.elems[outputs_index] = state.allocValue();
-            mkString(*(outputsVal->list.elems[outputs_index++]), o.first);
+            outputsVal->listElems()[outputs_index] = state.allocValue();
+            mkString(*(outputsVal->listElems()[outputs_index++]), o.first);
         }
         w.attrs->sort();
         Value fun;
@@ -186,7 +188,7 @@ static void prim_typeOf(EvalState & state, const Pos & pos, Value * * args, Valu
         case tPath: t = "path"; break;
         case tNull: t = "null"; break;
         case tAttrs: t = "set"; break;
-        case tList: t = "list"; break;
+        case tList1: case tList2: case tListN: t = "list"; break;
         case tLambda:
         case tPrimOp:
         case tPrimOpApp:
@@ -282,8 +284,8 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
     state.forceList(*startSet->value, pos);
 
     ValueList workSet;
-    for (unsigned int n = 0; n < startSet->value->list.length; ++n)
-        workSet.push_back(startSet->value->list.elems[n]);
+    for (unsigned int n = 0; n < startSet->value->listSize(); ++n)
+        workSet.push_back(startSet->value->listElems()[n]);
 
     /* Get the operator. */
     Bindings::iterator op =
@@ -321,17 +323,17 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
         state.forceList(call, pos);
 
         /* Add the values returned by the operator to the work set. */
-        for (unsigned int n = 0; n < call.list.length; ++n) {
-            state.forceValue(*call.list.elems[n]);
-            workSet.push_back(call.list.elems[n]);
+        for (unsigned int n = 0; n < call.listSize(); ++n) {
+            state.forceValue(*call.listElems()[n]);
+            workSet.push_back(call.listElems()[n]);
         }
     }
 
     /* Create the result list. */
     state.mkList(v, res.size());
     unsigned int n = 0;
-    foreach (ValueList::iterator, i, res)
-        v.list.elems[n++] = *i;
+    for (auto & i : res)
+        v.listElems()[n++] = i;
 }
 
 
@@ -477,24 +479,24 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
     StringSet outputs;
     outputs.insert("out");
 
-    foreach (Bindings::iterator, i, *args[0]->attrs) {
-        if (i->name == state.sIgnoreNulls) continue;
-        string key = i->name;
+    for (auto & i : *args[0]->attrs) {
+        if (i.name == state.sIgnoreNulls) continue;
+        string key = i.name;
         startNest(nest, lvlVomit, format("processing attribute ‘%1%’") % key);
 
         try {
 
             if (ignoreNulls) {
-                state.forceValue(*i->value);
-                if (i->value->type == tNull) continue;
+                state.forceValue(*i.value);
+                if (i.value->type == tNull) continue;
             }
 
             /* The `args' attribute is special: it supplies the
                command-line arguments to the builder. */
             if (key == "args") {
-                state.forceList(*i->value, pos);
-                for (unsigned int n = 0; n < i->value->list.length; ++n) {
-                    string s = state.coerceToString(posDrvName, *i->value->list.elems[n], context, true);
+                state.forceList(*i.value, pos);
+                for (unsigned int n = 0; n < i.value->listSize(); ++n) {
+                    string s = state.coerceToString(posDrvName, *i.value->listElems()[n], context, true);
                     drv.args.push_back(s);
                 }
             }
@@ -502,11 +504,11 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
             /* All other attributes are passed to the builder through
                the environment. */
             else {
-                string s = state.coerceToString(posDrvName, *i->value, context, true);
+                string s = state.coerceToString(posDrvName, *i.value, context, true);
                 drv.env[key] = s;
                 if (key == "builder") drv.builder = s;
-                else if (i->name == state.sSystem) drv.platform = s;
-                else if (i->name == state.sName) {
+                else if (i.name == state.sSystem) drv.platform = s;
+                else if (i.name == state.sName) {
                     drvName = s;
                     printMsg(lvlVomit, format("derivation name is ‘%1%’") % drvName);
                 }
@@ -520,17 +522,17 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
                 else if (key == "outputs") {
                     Strings tmp = tokenizeString<Strings>(s);
                     outputs.clear();
-                    foreach (Strings::iterator, j, tmp) {
-                        if (outputs.find(*j) != outputs.end())
-                            throw EvalError(format("duplicate derivation output ‘%1%’, at %2%") % *j % posDrvName);
-                        /* !!! Check whether *j is a valid attribute
+                    for (auto & j : tmp) {
+                        if (outputs.find(j) != outputs.end())
+                            throw EvalError(format("duplicate derivation output ‘%1%’, at %2%") % j % posDrvName);
+                        /* !!! Check whether j is a valid attribute
                            name. */
                         /* Derivations cannot be named ‘drv’, because
                            then we'd have an attribute ‘drvPath’ in
                            the resulting set. */
-                        if (*j == "drv")
+                        if (j == "drv")
                             throw EvalError(format("invalid derivation output name ‘drv’, at %1%") % posDrvName);
-                        outputs.insert(*j);
+                        outputs.insert(j);
                     }
                     if (outputs.empty())
                         throw EvalError(format("derivation cannot have an empty set of outputs, at %1%") % posDrvName);
@@ -547,8 +549,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
     /* Everything in the context of the strings in the derivation
        attributes should be added as dependencies of the resulting
        derivation. */
-    foreach (PathSet::iterator, i, context) {
-        Path path = *i;
+    for (auto & path : context) {
 
         /* Paths marked with `=' denote that the path of a derivation
            is explicitly passed to the builder.  Since that allows the
@@ -560,10 +561,10 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
         if (path.at(0) == '=') {
             /* !!! This doesn't work if readOnlyMode is set. */
             PathSet refs; computeFSClosure(*store, string(path, 1), refs);
-            foreach (PathSet::iterator, j, refs) {
-                drv.inputSrcs.insert(*j);
-                if (isDerivation(*j))
-                    drv.inputDrvs[*j] = store->queryDerivationOutputNames(*j);
+            for (auto & j : refs) {
+                drv.inputSrcs.insert(j);
+                if (isDerivation(j))
+                    drv.inputDrvs[j] = store->queryDerivationOutputNames(j);
             }
         }
 
@@ -622,20 +623,20 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
            are empty, and the corresponding environment variables have
            an empty value.  This ensures that changes in the set of
            output names do get reflected in the hash. */
-        foreach (StringSet::iterator, i, outputs) {
-            drv.env[*i] = "";
-            drv.outputs[*i] = DerivationOutput("", "", "");
+        for (auto & i : outputs) {
+            drv.env[i] = "";
+            drv.outputs[i] = DerivationOutput("", "", "");
         }
 
         /* Use the masked derivation expression to compute the output
            path. */
         Hash h = hashDerivationModulo(*store, drv);
 
-        foreach (DerivationOutputs::iterator, i, drv.outputs)
-            if (i->second.path == "") {
-                Path outPath = makeOutputPath(i->first, h, drvName);
-                drv.env[i->first] = outPath;
-                i->second.path = outPath;
+        for (auto & i : drv.outputs)
+            if (i.second.path == "") {
+                Path outPath = makeOutputPath(i.first, h, drvName);
+                drv.env[i.first] = outPath;
+                i.second.path = outPath;
             }
     }
 
@@ -652,9 +653,9 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
 
     state.mkAttrs(v, 1 + drv.outputs.size());
     mkString(*state.allocAttr(v, state.sDrvPath), drvPath, singleton<PathSet>("=" + drvPath));
-    foreach (DerivationOutputs::iterator, i, drv.outputs) {
-        mkString(*state.allocAttr(v, state.symbols.create(i->first)),
-            i->second.path, singleton<PathSet>("!" + i->first + "!" + drvPath));
+    for (auto & i : drv.outputs) {
+        mkString(*state.allocAttr(v, state.symbols.create(i.first)),
+            i.second.path, singleton<PathSet>("!" + i.first + "!" + drvPath));
     }
     v.attrs->sort();
 }
@@ -765,8 +766,8 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va
     SearchPath searchPath;
 
     PathSet context;
-    for (unsigned int n = 0; n < args[0]->list.length; ++n) {
-        Value & v2(*args[0]->list.elems[n]);
+    for (unsigned int n = 0; n < args[0]->listSize(); ++n) {
+        Value & v2(*args[0]->listElems()[n]);
         state.forceAttrs(v2, pos);
 
         string prefix;
@@ -871,11 +872,14 @@ static void prim_toFile(EvalState & state, const Pos & pos, Value * * args, Valu
 
     PathSet refs;
 
-    foreach (PathSet::iterator, i, context) {
-        Path path = *i;
+    for (auto path : context) {
         if (path.at(0) == '=') path = string(path, 1);
-        if (isDerivation(path))
-            throw EvalError(format("in ‘toFile’: the file ‘%1%’ cannot refer to derivation outputs, at %2%") % name % pos);
+        if (isDerivation(path)) {
+            /* See prim_unsafeDiscardOutputDependency. */
+            if (path.at(0) != '~')
+                throw EvalError(format("in ‘toFile’: the file ‘%1%’ cannot refer to derivation outputs, at %2%") % name % pos);
+            path = string(path, 1);
+	}
         refs.insert(path);
     }
 
@@ -968,9 +972,9 @@ static void prim_attrNames(EvalState & state, const Pos & pos, Value * * args, V
 
     unsigned int n = 0;
     for (auto & i : *args[0]->attrs)
-        mkString(*(v.list.elems[n++] = state.allocValue()), i.name);
+        mkString(*(v.listElems()[n++] = state.allocValue()), i.name);
 
-    std::sort(v.list.elems, v.list.elems + n,
+    std::sort(v.listElems(), v.listElems() + n,
         [](Value * v1, Value * v2) { return strcmp(v1->string.s, v2->string.s) < 0; });
 }
 
@@ -985,13 +989,13 @@ static void prim_attrValues(EvalState & state, const Pos & pos, Value * * args,
 
     unsigned int n = 0;
     for (auto & i : *args[0]->attrs)
-        v.list.elems[n++] = (Value *) &i;
+        v.listElems()[n++] = (Value *) &i;
 
-    std::sort(v.list.elems, v.list.elems + n,
+    std::sort(v.listElems(), v.listElems() + n,
         [](Value * v1, Value * v2) { return (string) ((Attr *) v1)->name < (string) ((Attr *) v2)->name; });
 
     for (unsigned int i = 0; i < n; ++i)
-        v.list.elems[i] = ((Attr *) v.list.elems[i])->value;
+        v.listElems()[i] = ((Attr *) v.listElems()[i])->value;
 }
 
 
@@ -1048,18 +1052,18 @@ static void prim_removeAttrs(EvalState & state, const Pos & pos, Value * * args,
 
     /* Get the attribute names to be removed. */
     std::set<Symbol> names;
-    for (unsigned int i = 0; i < args[1]->list.length; ++i) {
-        state.forceStringNoCtx(*args[1]->list.elems[i], pos);
-        names.insert(state.symbols.create(args[1]->list.elems[i]->string.s));
+    for (unsigned int i = 0; i < args[1]->listSize(); ++i) {
+        state.forceStringNoCtx(*args[1]->listElems()[i], pos);
+        names.insert(state.symbols.create(args[1]->listElems()[i]->string.s));
     }
 
     /* 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
        vector. */
     state.mkAttrs(v, args[0]->attrs->size());
-    foreach (Bindings::iterator, i, *args[0]->attrs) {
-        if (names.find(i->name) == names.end())
-            v.attrs->push_back(*i);
+    for (auto & i : *args[0]->attrs) {
+        if (names.find(i.name) == names.end())
+            v.attrs->push_back(i);
     }
 }
 
@@ -1073,12 +1077,12 @@ static void prim_listToAttrs(EvalState & state, const Pos & pos, Value * * args,
 {
     state.forceList(*args[0], pos);
 
-    state.mkAttrs(v, args[0]->list.length);
+    state.mkAttrs(v, args[0]->listSize());
 
     std::set<Symbol> seen;
 
-    for (unsigned int i = 0; i < args[0]->list.length; ++i) {
-        Value & v2(*args[0]->list.elems[i]);
+    for (unsigned int i = 0; i < args[0]->listSize(); ++i) {
+        Value & v2(*args[0]->listElems()[i]);
         state.forceAttrs(v2, pos);
 
         Bindings::iterator j = v2.attrs->find(state.sName);
@@ -1111,8 +1115,8 @@ static void prim_intersectAttrs(EvalState & state, const Pos & pos, Value * * ar
 
     state.mkAttrs(v, std::min(args[0]->attrs->size(), args[1]->attrs->size()));
 
-    foreach (Bindings::iterator, i, *args[0]->attrs) {
-        Bindings::iterator j = args[1]->attrs->find(i->name);
+    for (auto & i : *args[0]->attrs) {
+        Bindings::iterator j = args[1]->attrs->find(i.name);
         if (j != args[1]->attrs->end())
             v.attrs->push_back(*j);
     }
@@ -1131,11 +1135,11 @@ static void prim_catAttrs(EvalState & state, const Pos & pos, Value * * args, Va
     Symbol attrName = state.symbols.create(state.forceStringNoCtx(*args[0], pos));
     state.forceList(*args[1], pos);
 
-    Value * res[args[1]->list.length];
+    Value * res[args[1]->listSize()];
     unsigned int found = 0;
 
-    for (unsigned int n = 0; n < args[1]->list.length; ++n) {
-        Value & v2(*args[1]->list.elems[n]);
+    for (unsigned int n = 0; n < args[1]->listSize(); ++n) {
+        Value & v2(*args[1]->listElems()[n]);
         state.forceAttrs(v2, pos);
         Bindings::iterator i = v2.attrs->find(attrName);
         if (i != v2.attrs->end())
@@ -1144,7 +1148,7 @@ static void prim_catAttrs(EvalState & state, const Pos & pos, Value * * args, Va
 
     state.mkList(v, found);
     for (unsigned int n = 0; n < found; ++n)
-        v.list.elems[n] = res[n];
+        v.listElems()[n] = res[n];
 }
 
 
@@ -1173,9 +1177,9 @@ static void prim_functionArgs(EvalState & state, const Pos & pos, Value * * args
     }
 
     state.mkAttrs(v, args[0]->lambda.fun->formals->formals.size());
-    foreach (Formals::Formals_::iterator, i, args[0]->lambda.fun->formals->formals)
+    for (auto & i : args[0]->lambda.fun->formals->formals)
         // !!! should optimise booleans (allocate only once)
-        mkBool(*state.allocAttr(v, i->name), i->def);
+        mkBool(*state.allocAttr(v, i.name), i.def);
     v.attrs->sort();
 }
 
@@ -1189,17 +1193,17 @@ static void prim_functionArgs(EvalState & state, const Pos & pos, Value * * args
 static void prim_isList(EvalState & state, const Pos & pos, Value * * args, Value & v)
 {
     state.forceValue(*args[0]);
-    mkBool(v, args[0]->type == tList);
+    mkBool(v, args[0]->isList());
 }
 
 
 static void elemAt(EvalState & state, const Pos & pos, Value & list, int n, Value & v)
 {
     state.forceList(list, pos);
-    if (n < 0 || (unsigned int) n >= list.list.length)
+    if (n < 0 || (unsigned int) n >= list.listSize())
         throw Error(format("list index %1% is out of bounds, at %2%") % n % pos);
-    state.forceValue(*list.list.elems[n]);
-    v = *list.list.elems[n];
+    state.forceValue(*list.listElems()[n]);
+    v = *list.listElems()[n];
 }
 
 
@@ -1223,11 +1227,11 @@ static void prim_head(EvalState & state, const Pos & pos, Value * * args, Value
 static void prim_tail(EvalState & state, const Pos & pos, Value * * args, Value & v)
 {
     state.forceList(*args[0], pos);
-    if (args[0]->list.length == 0)
+    if (args[0]->listSize() == 0)
         throw Error(format("‘tail’ called on an empty list, at %1%") % pos);
-    state.mkList(v, args[0]->list.length - 1);
-    for (unsigned int n = 0; n < v.list.length; ++n)
-        v.list.elems[n] = args[0]->list.elems[n + 1];
+    state.mkList(v, args[0]->listSize() - 1);
+    for (unsigned int n = 0; n < v.listSize(); ++n)
+        v.listElems()[n] = args[0]->listElems()[n + 1];
 }
 
 
@@ -1237,11 +1241,11 @@ static void prim_map(EvalState & state, const Pos & pos, Value * * args, Value &
     state.forceFunction(*args[0], pos);
     state.forceList(*args[1], pos);
 
-    state.mkList(v, args[1]->list.length);
+    state.mkList(v, args[1]->listSize());
 
-    for (unsigned int n = 0; n < v.list.length; ++n)
-        mkApp(*(v.list.elems[n] = state.allocValue()),
-            *args[0], *args[1]->list.elems[n]);
+    for (unsigned int n = 0; n < v.listSize(); ++n)
+        mkApp(*(v.listElems()[n] = state.allocValue()),
+            *args[0], *args[1]->listElems()[n]);
 }
 
 
@@ -1254,15 +1258,15 @@ static void prim_filter(EvalState & state, const Pos & pos, Value * * args, Valu
     state.forceList(*args[1], pos);
 
     // FIXME: putting this on the stack is risky.
-    Value * vs[args[1]->list.length];
+    Value * vs[args[1]->listSize()];
     unsigned int k = 0;
 
     bool same = true;
-    for (unsigned int n = 0; n < args[1]->list.length; ++n) {
+    for (unsigned int n = 0; n < args[1]->listSize(); ++n) {
         Value res;
-        state.callFunction(*args[0], *args[1]->list.elems[n], res, noPos);
+        state.callFunction(*args[0], *args[1]->listElems()[n], res, noPos);
         if (state.forceBool(res))
-            vs[k++] = args[1]->list.elems[n];
+            vs[k++] = args[1]->listElems()[n];
         else
             same = false;
     }
@@ -1271,7 +1275,7 @@ static void prim_filter(EvalState & state, const Pos & pos, Value * * args, Valu
         v = *args[1];
     else {
         state.mkList(v, k);
-        for (unsigned int n = 0; n < k; ++n) v.list.elems[n] = vs[n];
+        for (unsigned int n = 0; n < k; ++n) v.listElems()[n] = vs[n];
     }
 }
 
@@ -1281,8 +1285,8 @@ static void prim_elem(EvalState & state, const Pos & pos, Value * * args, Value
 {
     bool res = false;
     state.forceList(*args[1], pos);
-    for (unsigned int n = 0; n < args[1]->list.length; ++n)
-        if (state.eqValues(*args[0], *args[1]->list.elems[n])) {
+    for (unsigned int n = 0; n < args[1]->listSize(); ++n)
+        if (state.eqValues(*args[0], *args[1]->listElems()[n])) {
             res = true;
             break;
         }
@@ -1294,7 +1298,7 @@ static void prim_elem(EvalState & state, const Pos & pos, Value * * args, Value
 static void prim_concatLists(EvalState & state, const Pos & pos, Value * * args, Value & v)
 {
     state.forceList(*args[0], pos);
-    state.concatLists(v, args[0]->list.length, args[0]->list.elems, pos);
+    state.concatLists(v, args[0]->listSize(), args[0]->listElems(), pos);
 }
 
 
@@ -1302,7 +1306,61 @@ static void prim_concatLists(EvalState & state, const Pos & pos, Value * * args,
 static void prim_length(EvalState & state, const Pos & pos, Value * * args, Value & v)
 {
     state.forceList(*args[0], pos);
-    mkInt(v, args[0]->list.length);
+    mkInt(v, args[0]->listSize());
+}
+
+
+/* Reduce a list by applying a binary operator, from left to
+   right. The operator is applied strictly. */
+static void prim_foldlStrict(EvalState & state, const Pos & pos, Value * * args, Value & v)
+{
+    state.forceFunction(*args[0], pos);
+    state.forceList(*args[2], pos);
+
+    Value * vCur = args[1];
+
+    if (args[2]->listSize())
+        for (unsigned int n = 0; n < args[2]->listSize(); ++n) {
+            Value vTmp;
+            state.callFunction(*args[0], *vCur, vTmp, pos);
+            vCur = n == args[2]->listSize() - 1 ? &v : state.allocValue();
+            state.callFunction(vTmp, *args[2]->listElems()[n], *vCur, pos);
+        }
+    else
+        v = *vCur;
+
+    state.forceValue(v);
+}
+
+
+static void anyOrAll(bool any, EvalState & state, const Pos & pos, Value * * args, Value & v)
+{
+    state.forceFunction(*args[0], pos);
+    state.forceList(*args[1], pos);
+
+    Value vTmp;
+    for (unsigned int n = 0; n < args[1]->listSize(); ++n) {
+        state.callFunction(*args[0], *args[1]->listElems()[n], vTmp, pos);
+        bool res = state.forceBool(vTmp);
+        if (res == any) {
+            mkBool(v, any);
+            return;
+        }
+    }
+
+    mkBool(v, !any);
+}
+
+
+static void prim_any(EvalState & state, const Pos & pos, Value * * args, Value & v)
+{
+    anyOrAll(true, state, pos, args, v);
+}
+
+
+static void prim_all(EvalState & state, const Pos & pos, Value * * args, Value & v)
+{
+    anyOrAll(false, state, pos, args, v);
 }
 
 
@@ -1407,11 +1465,8 @@ static void prim_unsafeDiscardOutputDependency(EvalState & state, const Pos & po
     string s = state.coerceToString(pos, *args[0], context);
 
     PathSet context2;
-    foreach (PathSet::iterator, i, context) {
-        Path p = *i;
-        if (p.at(0) == '=') p = "~" + string(p, 1);
-        context2.insert(p);
-    }
+    for (auto & p : context)
+        context2.insert(p.at(0) == '=' ? "~" + string(p, 1) : p);
 
     mkString(v, s, context2);
 }
@@ -1452,9 +1507,9 @@ static void prim_match(EvalState & state, const Pos & pos, Value * * args, Value
     for (unsigned int n = 0; n < len; ++n) {
         auto i = subs.find(n);
         if (i == subs.end())
-            mkNull(*(v.list.elems[n] = state.allocValue()));
+            mkNull(*(v.listElems()[n] = state.allocValue()));
         else
-            mkString(*(v.list.elems[n] = state.allocValue()), i->second);
+            mkString(*(v.listElems()[n] = state.allocValue()), i->second);
     }
 }
 
@@ -1646,6 +1701,9 @@ void EvalState::createBaseEnv()
     addPrimOp("__elem", 2, prim_elem);
     addPrimOp("__concatLists", 1, prim_concatLists);
     addPrimOp("__length", 1, prim_length);
+    addPrimOp("__foldl'", 3, prim_foldlStrict);
+    addPrimOp("__any", 2, prim_any);
+    addPrimOp("__all", 2, prim_all);
 
     // Integer arithmetic
     addPrimOp("__add", 2, prim_add);
@@ -1685,7 +1743,7 @@ void EvalState::createBaseEnv()
     mkList(v, searchPath.size());
     int n = 0;
     for (auto & i : searchPath) {
-        v2 = v.list.elems[n++] = allocValue();
+        v2 = v.listElems()[n++] = allocValue();
         mkAttrs(*v2, 2);
         mkString(*allocAttr(*v2, symbols.create("path")), i.second);
         mkString(*allocAttr(*v2, symbols.create("prefix")), i.first);
diff --git a/src/libexpr/value-to-json.cc b/src/libexpr/value-to-json.cc
index cdb71341875a..b0cf85e21f18 100644
--- a/src/libexpr/value-to-json.cc
+++ b/src/libexpr/value-to-json.cc
@@ -12,14 +12,14 @@ namespace nix {
 void escapeJSON(std::ostream & str, const string & s)
 {
     str << "\"";
-    foreach (string::const_iterator, i, s)
-        if (*i == '\"' || *i == '\\') str << "\\" << *i;
-        else if (*i == '\n') str << "\\n";
-        else if (*i == '\r') str << "\\r";
-        else if (*i == '\t') str << "\\t";
-        else if (*i >= 0 && *i < 32)
-            str << "\\u" << std::setfill('0') << std::setw(4) << std::hex << (uint16_t) *i << std::dec;
-        else str << *i;
+    for (auto & i : s)
+        if (i == '\"' || i == '\\') str << "\\" << i;
+        else if (i == '\n') str << "\\n";
+        else if (i == '\r') str << "\\r";
+        else if (i == '\t') str << "\\t";
+        else if (i >= 0 && i < 32)
+            str << "\\u" << std::setfill('0') << std::setw(4) << std::hex << (uint16_t) i << std::dec;
+        else str << i;
     str << "\"";
 }
 
@@ -59,11 +59,11 @@ void printValueAsJSON(EvalState & state, bool strict,
             if (i == v.attrs->end()) {
                 JSONObject json(str);
                 StringSet names;
-                foreach (Bindings::iterator, i, *v.attrs)
-                    names.insert(i->name);
-                foreach (StringSet::iterator, i, names) {
-                    Attr & a(*v.attrs->find(state.symbols.create(*i)));
-                    json.attr(*i);
+                for (auto & j : *v.attrs)
+                    names.insert(j.name);
+                for (auto & j : names) {
+                    Attr & a(*v.attrs->find(state.symbols.create(j)));
+                    json.attr(j);
                     printValueAsJSON(state, strict, *a.value, str, context);
                 }
             } else
@@ -71,16 +71,16 @@ void printValueAsJSON(EvalState & state, bool strict,
             break;
         }
 
-        case tList: {
+        case tList1: case tList2: case tListN: {
             JSONList json(str);
-            for (unsigned int n = 0; n < v.list.length; ++n) {
+            for (unsigned int n = 0; n < v.listSize(); ++n) {
                 json.elem();
-                printValueAsJSON(state, strict, *v.list.elems[n], str, context);
+                printValueAsJSON(state, strict, *v.listElems()[n], str, context);
             }
             break;
         }
 
-	case tExternal:
+        case tExternal:
             v.external->printValueAsJSON(state, strict, str, context);
             break;
 
diff --git a/src/libexpr/value-to-xml.cc b/src/libexpr/value-to-xml.cc
index bbbb7039bf70..cb52ce1e67bd 100644
--- a/src/libexpr/value-to-xml.cc
+++ b/src/libexpr/value-to-xml.cc
@@ -8,7 +8,7 @@
 
 namespace nix {
 
-    
+
 static XMLAttrs singletonAttrs(const string & name, const string & value)
 {
     XMLAttrs attrs;
@@ -33,17 +33,17 @@ static void showAttrs(EvalState & state, bool strict, bool location,
     Bindings & attrs, XMLWriter & doc, PathSet & context, PathSet & drvsSeen)
 {
     StringSet names;
-    
-    foreach (Bindings::iterator, i, attrs)
-        names.insert(i->name);
-    
-    foreach (StringSet::iterator, i, names) {
-        Attr & a(*attrs.find(state.symbols.create(*i)));
-        
+
+    for (auto & i : attrs)
+        names.insert(i.name);
+
+    for (auto & i : names) {
+        Attr & a(*attrs.find(state.symbols.create(i)));
+
         XMLAttrs xmlAttrs;
-        xmlAttrs["name"] = *i;
+        xmlAttrs["name"] = i;
         if (location && a.pos != &noPos) posToXML(xmlAttrs, *a.pos);
-        
+
         XMLOpenElement _(doc, "attr", xmlAttrs);
         printValueAsXML(state, strict, location,
             *a.value, doc, context, drvsSeen);
@@ -57,7 +57,7 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
     checkInterrupt();
 
     if (strict) state.forceValue(v);
-        
+
     switch (v.type) {
 
         case tInt:
@@ -85,7 +85,7 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
         case tAttrs:
             if (state.isDerivation(v)) {
                 XMLAttrs xmlAttrs;
-            
+
                 Bindings::iterator a = v.attrs->find(state.symbols.create("derivation"));
 
                 Path drvPath;
@@ -95,7 +95,7 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
                     if (a->value->type == tString)
                         xmlAttrs["drvPath"] = drvPath = a->value->string.s;
                 }
-        
+
                 a = v.attrs->find(state.sOutPath);
                 if (a != v.attrs->end()) {
                     if (strict) state.forceValue(*a->value);
@@ -116,13 +116,13 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
                 XMLOpenElement _(doc, "attrs");
                 showAttrs(state, strict, location, *v.attrs, doc, context, drvsSeen);
             }
-            
+
             break;
 
-        case tList: {
+        case tList1: case tList2: case tListN: {
             XMLOpenElement _(doc, "list");
-            for (unsigned int n = 0; n < v.list.length; ++n)
-                printValueAsXML(state, strict, location, *v.list.elems[n], doc, context, drvsSeen);
+            for (unsigned int n = 0; n < v.listSize(); ++n)
+                printValueAsXML(state, strict, location, *v.listElems()[n], doc, context, drvsSeen);
             break;
         }
 
@@ -130,17 +130,17 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
             XMLAttrs xmlAttrs;
             if (location) posToXML(xmlAttrs, v.lambda.fun->pos);
             XMLOpenElement _(doc, "function", xmlAttrs);
-            
+
             if (v.lambda.fun->matchAttrs) {
                 XMLAttrs attrs;
                 if (!v.lambda.fun->arg.empty()) attrs["name"] = v.lambda.fun->arg;
                 if (v.lambda.fun->formals->ellipsis) attrs["ellipsis"] = "1";
                 XMLOpenElement _(doc, "attrspat", attrs);
-                foreach (Formals::Formals_::iterator, i, v.lambda.fun->formals->formals)
-                    doc.writeEmptyElement("attr", singletonAttrs("name", i->name));
+                for (auto & i : v.lambda.fun->formals->formals)
+                    doc.writeEmptyElement("attr", singletonAttrs("name", i.name));
             } else
                 doc.writeEmptyElement("varpat", singletonAttrs("name", v.lambda.fun->arg));
-            
+
             break;
         }
 
@@ -166,9 +166,9 @@ void printValueAsXML(EvalState & state, bool strict, bool location,
 {
     XMLWriter doc(true, out);
     XMLOpenElement root(doc, "expr");
-    PathSet drvsSeen;    
+    PathSet drvsSeen;
     printValueAsXML(state, strict, location, v, doc, context, drvsSeen);
 }
 
- 
+
 }
diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh
index c06b5a6d1153..e6d1502cb607 100644
--- a/src/libexpr/value.hh
+++ b/src/libexpr/value.hh
@@ -12,7 +12,9 @@ typedef enum {
     tPath,
     tNull,
     tAttrs,
-    tList,
+    tList1,
+    tList2,
+    tListN,
     tThunk,
     tApp,
     tLambda,
@@ -119,9 +121,10 @@ struct Value
         const char * path;
         Bindings * attrs;
         struct {
-            unsigned int length;
+            unsigned int size;
             Value * * elems;
-        } list;
+        } bigList;
+        Value * smallList[2];
         struct {
             Env * env;
             Expr * expr;
@@ -139,6 +142,26 @@ struct Value
         } primOpApp;
         ExternalValueBase * external;
     };
+
+    bool isList() const
+    {
+        return type == tList1 || type == tList2 || type == tListN;
+    }
+
+    Value * * listElems()
+    {
+        return type == tList1 || type == tList2 ? smallList : bigList.elems;
+    }
+
+    const Value * const * listElems() const
+    {
+        return type == tList1 || type == tList2 ? smallList : bigList.elems;
+    }
+
+    unsigned int listSize() const
+    {
+        return type == tList1 ? 1 : type == tList2 ? 2 : bigList.size;
+    }
 };