about summary refs log tree commit diff
path: root/lib
diff options
context:
space:
mode:
authorJohn Ericson <git@JohnEricson.me>2019-09-02 01:31:31 -0400
committerJohn Ericson <git@JohnEricson.me>2019-09-02 01:31:31 -0400
commitc33d80c071927d783bd10943e723ed408ec89e48 (patch)
treeee0d82edaae37f797995468b8dbd5ce4beffab27 /lib
parent446f8c851d599326373a2c910841d092ff8e68ae (diff)
parent74b42e28b94cc42f93b50c5aeee8794f66795fd0 (diff)
Merge remote-tracking branch 'upstream/master' into feature/js-unknown-ghcjs
Diffstat (limited to 'lib')
-rw-r--r--lib/default.nix10
-rw-r--r--lib/fixed-points.nix9
-rw-r--r--lib/generators.nix2
-rw-r--r--lib/licenses.nix21
-rw-r--r--lib/lists.nix18
-rw-r--r--lib/modules.nix50
-rw-r--r--lib/options.nix8
-rw-r--r--lib/sources.nix24
-rw-r--r--lib/strings.nix2
-rw-r--r--lib/systems/default.nix31
-rw-r--r--lib/systems/doubles.nix18
-rw-r--r--lib/systems/examples.nix49
-rw-r--r--lib/systems/for-meta.nix37
-rw-r--r--lib/systems/inspect.nix4
-rw-r--r--lib/systems/parse.nix123
-rw-r--r--lib/systems/platforms.nix11
-rwxr-xr-xlib/tests/modules.sh11
-rw-r--r--lib/tests/modules/alias-with-priority-can-override.nix9
-rw-r--r--lib/tests/modules/alias-with-priority.nix5
-rw-r--r--lib/tests/modules/declare-either.nix5
-rw-r--r--lib/tests/modules/declare-oneOf.nix9
-rw-r--r--lib/tests/release.nix15
-rw-r--r--lib/tests/systems.nix12
-rw-r--r--lib/trivial.nix7
-rw-r--r--lib/types.nix26
25 files changed, 339 insertions, 177 deletions
diff --git a/lib/default.nix b/lib/default.nix
index d400907ebb0cb..18d2dfae1e183 100644
--- a/lib/default.nix
+++ b/lib/default.nix
@@ -50,7 +50,7 @@ let
     filesystem = callLibs ./filesystem.nix;
 
     # back-compat aliases
-    platforms = systems.forMeta;
+    platforms = systems.doubles;
 
     inherit (builtins) add addErrorContext attrNames concatLists
       deepSeq elem elemAt filter genericClosure genList getAttr
@@ -59,7 +59,7 @@ let
       stringLength sub substring tail;
     inherit (trivial) id const concat or and bitAnd bitOr bitXor bitNot
       boolToString mergeAttrs flip mapNullable inNixShell min max
-      importJSON warn info nixpkgsVersion version mod compare
+      importJSON warn info showWarnings nixpkgsVersion version mod compare
       splitByAndCompare functionArgs setFunctionArgs isFunction;
     inherit (fixedPoints) fix fix' converge extends composeExtensions
       makeExtensible makeExtensibleWithCustomName;
@@ -71,7 +71,7 @@ let
       zipAttrsWithNames zipAttrsWith zipAttrs recursiveUpdateUntil
       recursiveUpdate matchAttrs overrideExisting getOutput getBin
       getLib getDev chooseDevOutputs zipWithNames zip;
-    inherit (lists) singleton foldr fold foldl foldl' imap0 imap1
+    inherit (lists) singleton forEach foldr fold foldl foldl' imap0 imap1
       concatMap flatten remove findSingle findFirst any all count
       optional optionals toList range partition zipListsWith zipLists
       reverseList listDfs toposort sort naturalSort compareLists take
@@ -81,7 +81,7 @@ let
       intersperse concatStringsSep concatMapStringsSep
       concatImapStringsSep makeSearchPath makeSearchPathOutput
       makeLibraryPath makeBinPath optionalString
-      hasPrefix hasSuffix stringToCharacters stringAsChars escape
+      hasInfix hasPrefix hasSuffix stringToCharacters stringAsChars escape
       escapeShellArg escapeShellArgs replaceChars lowerChars
       upperChars toLower toUpper addContextFrom splitString
       removePrefix removeSuffix versionOlder versionAtLeast getVersion
@@ -109,7 +109,7 @@ let
       mkFixStrictness mkOrder mkBefore mkAfter mkAliasDefinitions
       mkAliasAndWrapDefinitions fixMergeModules mkRemovedOptionModule
       mkRenamedOptionModule mkMergedOptionModule mkChangedOptionModule
-      mkAliasOptionModule mkAliasOptionModuleWithPriority doRename filterModules;
+      mkAliasOptionModule doRename filterModules;
     inherit (options) isOption mkEnableOption mkSinkUndeclaredOptions
       mergeDefaultOption mergeOneOption mergeEqualOption getValues
       getFiles optionAttrSetToDocList optionAttrSetToDocList'
diff --git a/lib/fixed-points.nix b/lib/fixed-points.nix
index 2f818c88de5db..968930526a639 100644
--- a/lib/fixed-points.nix
+++ b/lib/fixed-points.nix
@@ -30,9 +30,12 @@ rec {
   #     nix-repl> converge (x: x / 2) 16
   #     0
   converge = f: x:
-    if (f x) == x
-    then x
-    else converge f (f x);
+    let
+      x' = f x;
+    in
+      if x' == x
+      then x
+      else converge f x';
 
   # Modify the contents of an explicitly recursive attribute set in a way that
   # honors `self`-references. This is accomplished with a function
diff --git a/lib/generators.nix b/lib/generators.nix
index 863ba847423ee..a71654bec6c36 100644
--- a/lib/generators.nix
+++ b/lib/generators.nix
@@ -178,7 +178,7 @@ rec {
   toPlist = {}: v: let
     isFloat = builtins.isFloat or (x: false);
     expr = ind: x:  with builtins;
-      if isNull x   then "" else
+      if x == null  then "" else
       if isBool x   then bool ind x else
       if isInt x    then int ind x else
       if isString x then str ind x else
diff --git a/lib/licenses.nix b/lib/licenses.nix
index b022d8bc11bf6..c964060d2a5c8 100644
--- a/lib/licenses.nix
+++ b/lib/licenses.nix
@@ -145,6 +145,12 @@ lib.mapAttrs (n: v: v // { shortName = n; }) rec {
     free = false;
   };
 
+  cc-by-nc-30 = spdx {
+    spdxId = "CC-BY-NC-3.0";
+    fullName = "Creative Commons Attribution Non Commercial 3.0 Unported";
+    free = false;
+  };
+
   cc-by-nc-40 = spdx {
     spdxId = "CC-BY-NC-4.0";
     fullName = "Creative Commons Attribution Non Commercial 4.0 International";
@@ -428,12 +434,12 @@ lib.mapAttrs (n: v: v // { shortName = n; }) rec {
 
   lgpl21 = spdx {
     spdxId = "LGPL-2.1-only";
-    fullName = "GNU Library General Public License v2.1 only";
+    fullName = "GNU Lesser General Public License v2.1 only";
   };
 
   lgpl21Plus = spdx {
     spdxId = "LGPL-2.1-or-later";
-    fullName = "GNU Library General Public License v2.1 or later";
+    fullName = "GNU Lesser General Public License v2.1 or later";
   };
 
   lgpl3 = spdx {
@@ -451,9 +457,9 @@ lib.mapAttrs (n: v: v // { shortName = n; }) rec {
     fullName = "libpng License";
   };
 
-  libpng2 = {
-    fullName = "libpng License v2"; # 1.6.36+
-    url = "http://www.libpng.org/pub/png/src/libpng-LICENSE.txt";
+  libpng2 = spdx {
+    spdxId = "libpng-2.0"; # Used since libpng 1.6.36.
+    fullName = "PNG Reference Library version 2";
   };
 
   libtiff = spdx {
@@ -561,6 +567,11 @@ lib.mapAttrs (n: v: v // { shortName = n; }) rec {
     fullName = "OpenSSL License";
   };
 
+  osl2 = spdx {
+    spdxId = "OSL-2.0";
+    fullName = "Open Software License 2.0";
+  };
+
   osl21 = spdx {
     spdxId = "OSL-2.1";
     fullName = "Open Software License 2.1";
diff --git a/lib/lists.nix b/lib/lists.nix
index be541427c2470..e4fcf959b60c8 100644
--- a/lib/lists.nix
+++ b/lib/lists.nix
@@ -7,7 +7,7 @@ let
 in
 rec {
 
-  inherit (builtins) head tail length isList elemAt concatLists filter elem genList;
+  inherit (builtins) head tail length isList elemAt concatLists filter elem genList map;
 
   /*  Create a list consisting of a single element.  `singleton x` is
       sometimes more convenient with respect to indentation than `[x]`
@@ -21,6 +21,19 @@ rec {
   */
   singleton = x: [x];
 
+  /*  Apply the function to each element in the list. Same as `map`, but arguments
+      flipped.
+
+      Type: forEach :: [a] -> (a -> b) -> [b]
+
+      Example:
+        forEach [ 1 2 ] (x:
+          toString x
+        )
+        => [ "1" "2" ]
+  */
+  forEach = xs: f: map f xs;
+
   /* “right fold” a binary function `op` between successive elements of
      `list` with `nul' as the starting value, i.e.,
      `foldr op nul [x_1 x_2 ... x_n] == op x_1 (op x_2 ... (op x_n nul))`.
@@ -633,8 +646,7 @@ rec {
     else
       let
         x = head list;
-        xs = unique (drop 1 list);
-      in [x] ++ remove x xs;
+      in [x] ++ unique (remove x list);
 
   /* Intersects list 'e' and another list. O(nm) complexity.
 
diff --git a/lib/modules.nix b/lib/modules.nix
index 5c9d66d8f97b9..c3c903c1dfa80 100644
--- a/lib/modules.nix
+++ b/lib/modules.nix
@@ -323,16 +323,14 @@ rec {
         else
           mergeDefinitions loc opt.type defs';
 
-      # Check whether the option is defined, and apply the ‘apply’
-      # function to the merged value.  This allows options to yield a
-      # value computed from the definitions.
-      value =
-        if !res.isDefined then
-          throw "The option `${showOption loc}' is used but not defined."
-        else if opt ? apply then
-          opt.apply res.mergedValue
-        else
-          res.mergedValue;
+
+      # The value with a check that it is defined
+      valueDefined = if res.isDefined then res.mergedValue else
+        throw "The option `${showOption loc}' is used but not defined.";
+
+      # Apply the 'apply' function to the merged value. This allows options to
+      # yield a value computed from the definitions
+      value = if opt ? apply then opt.apply valueDefined else valueDefined;
 
     in opt //
       { value = builtins.addErrorContext "while evaluating the option `${showOption loc}':" value;
@@ -476,8 +474,22 @@ rec {
      optionSet to options of type submodule.  FIXME: remove
      eventually. */
   fixupOptionType = loc: opt:
-    if opt.type.getSubModules or null == null
-      then opt // { type = opt.type or types.unspecified; }
+    let
+      options = opt.options or
+        (throw "Option `${showOption loc'}' has type optionSet but has no option attribute, in ${showFiles opt.declarations}.");
+      f = tp:
+        let optionSetIn = type: (tp.name == type) && (tp.functor.wrapped.name == "optionSet");
+        in
+        if tp.name == "option set" || tp.name == "submodule" then
+          throw "The option ${showOption loc} uses submodules without a wrapping type, in ${showFiles opt.declarations}."
+        else if optionSetIn "attrsOf" then types.attrsOf (types.submodule options)
+        else if optionSetIn "loaOf"   then types.loaOf   (types.submodule options)
+        else if optionSetIn "listOf"  then types.listOf  (types.submodule options)
+        else if optionSetIn "nullOr"  then types.nullOr  (types.submodule options)
+        else tp;
+    in
+      if opt.type.getSubModules or null == null
+      then opt // { type = f (opt.type or types.unspecified); }
       else opt // { type = opt.type.substSubModules opt.options; options = []; };
 
 
@@ -596,6 +608,9 @@ rec {
 
      forwards any definitions of boot.copyKernels to
      boot.loader.grub.copyKernels while printing a warning.
+
+     This also copies over the priority from the aliased option to the
+     non-aliased option.
   */
   mkRenamedOptionModule = from: to: doRename {
     inherit from to;
@@ -690,16 +705,7 @@ rec {
     use = id;
   };
 
-  /* Like ‘mkAliasOptionModule’, but copy over the priority of the option as well. */
-  mkAliasOptionModuleWithPriority = from: to: doRename {
-    inherit from to;
-    visible = true;
-    warn = false;
-    use = id;
-    withPriority = true;
-  };
-
-  doRename = { from, to, visible, warn, use, withPriority ? false }:
+  doRename = { from, to, visible, warn, use, withPriority ? true }:
     { config, options, ... }:
     let
       fromOpt = getAttrFromPath from options;
diff --git a/lib/options.nix b/lib/options.nix
index 5cea99067aaba..e5c0631a54373 100644
--- a/lib/options.nix
+++ b/lib/options.nix
@@ -36,7 +36,7 @@ rec {
     example ? null,
     # String describing the option.
     description ? null,
-    # Related packages used in the manual (see `genRelatedPackages` in ../nixos/doc/manual/default.nix).
+    # Related packages used in the manual (see `genRelatedPackages` in ../nixos/lib/make-options-doc/default.nix).
     relatedPackages ? null,
     # Option type, providing type-checking and value merging.
     type ? null,
@@ -48,6 +48,8 @@ rec {
     visible ? null,
     # Whether the option can be set only once
     readOnly ? null,
+    # Deprecated, used by types.optionSet.
+    options ? null
     } @ attrs:
     attrs // { _type = "option"; };
 
@@ -99,7 +101,7 @@ rec {
   mergeOneOption = loc: defs:
     if defs == [] then abort "This case should never happen."
     else if length defs != 1 then
-      throw "The unique option `${showOption loc}' is defined multiple times, in ${showFiles (getFiles defs)}."
+      throw "The unique option `${showOption loc}' is defined multiple times, in:\n - ${concatStringsSep "\n - " (getFiles defs)}."
     else (head defs).value;
 
   /* "Merge" option definitions by checking that they all have the same value. */
@@ -141,7 +143,7 @@ rec {
         docOption = rec {
           loc = opt.loc;
           name = showOption opt.loc;
-          description = opt.description or (throw "Option `${name}' has no description.");
+          description = opt.description or (lib.warn "Option `${name}' has no description." "This option has no description.");
           declarations = filter (x: x != unknownModule) opt.declarations;
           internal = opt.internal or false;
           visible = opt.visible or true;
diff --git a/lib/sources.nix b/lib/sources.nix
index 1a9f3f7d1f343..c4680087b2454 100644
--- a/lib/sources.nix
+++ b/lib/sources.nix
@@ -12,8 +12,8 @@ rec {
   # Bring in a path as a source, filtering out all Subversion and CVS
   # directories, as well as backup files (*~).
   cleanSourceFilter = name: type: let baseName = baseNameOf (toString name); in ! (
-    # Filter out Subversion and CVS directories.
-    (type == "directory" && (baseName == ".git" || baseName == ".svn" || baseName == "CVS" || baseName == ".hg")) ||
+    # Filter out version control software files/directories
+    (baseName == ".git" || type == "directory" && (baseName == ".svn" || baseName == "CVS" || baseName == ".hg")) ||
     # Filter out editor backup / swap files.
     lib.hasSuffix "~" baseName ||
     builtins.match "^\\.sw[a-z]$" baseName != null ||
@@ -53,12 +53,16 @@ rec {
   # Filter sources by a list of regular expressions.
   #
   # E.g. `src = sourceByRegex ./my-subproject [".*\.py$" "^database.sql$"]`
-  sourceByRegex = src: regexes: cleanSourceWith {
-    filter = (path: type:
-      let relPath = lib.removePrefix (toString src + "/") (toString path);
-      in lib.any (re: builtins.match re relPath != null) regexes);
-    inherit src;
-  };
+  sourceByRegex = src: regexes:
+    let
+      isFiltered = src ? _isLibCleanSourceWith;
+      origSrc = if isFiltered then src.origSrc else src;
+    in lib.cleanSourceWith {
+      filter = (path: type:
+        let relPath = lib.removePrefix (toString origSrc + "/") (toString path);
+        in lib.any (re: builtins.match re relPath != null) regexes);
+      inherit src;
+    };
 
   # Get all files ending with the specified suffices from the given
   # directory or its descendants.  E.g. `sourceFilesBySuffices ./dir
@@ -83,7 +87,7 @@ rec {
                  # Sometimes git stores the commitId directly in the file but
                  # sometimes it stores something like: «ref: refs/heads/branch-name»
                  matchRef    = match "^ref: (.*)$" fileContent;
-             in if   isNull matchRef
+             in if   matchRef == null
                 then fileContent
                 else readCommitFromFile (lib.head matchRef) path
            # Sometimes, the file isn't there at all and has been packed away in the
@@ -92,7 +96,7 @@ rec {
            then
              let fileContent = readFile packedRefsName;
                  matchRef    = match (".*\n([^\n ]*) " + file + "\n.*") fileContent;
-             in if   isNull matchRef
+             in if   matchRef == null
                 then throw ("Could not find " + file + " in " + packedRefsName)
                 else lib.head matchRef
            else throw ("Not a .git directory: " + path);
diff --git a/lib/strings.nix b/lib/strings.nix
index 47c881cfbc7cc..ae0d74c6721dc 100644
--- a/lib/strings.nix
+++ b/lib/strings.nix
@@ -90,7 +90,7 @@ rec {
   /* Same as `concatMapStringsSep`, but the mapping function
      additionally receives the position of its argument.
 
-     Type: concatMapStringsSep :: string -> (int -> string -> string) -> [string] -> string
+     Type: concatIMapStringsSep :: string -> (int -> string -> string) -> [string] -> string
 
      Example:
        concatImapStringsSep "-" (pos: x: toString (x / pos)) [ 6 6 6 ]
diff --git a/lib/systems/default.nix b/lib/systems/default.nix
index 77f200952958b..8aa413f53817b 100644
--- a/lib/systems/default.nix
+++ b/lib/systems/default.nix
@@ -3,7 +3,6 @@
 
 rec {
   doubles = import ./doubles.nix { inherit lib; };
-  forMeta = import ./for-meta.nix { inherit lib; };
   parse = import ./parse.nix { inherit lib; };
   inspect = import ./inspect.nix { inherit lib; };
   platforms = import ./platforms.nix { inherit lib; };
@@ -15,7 +14,9 @@ rec {
   # `parsed` is inferred from args, both because there are two options with one
   # clearly prefered, and to prevent cycles. A simpler fixed point where the RHS
   # always just used `final.*` would fail on both counts.
-  elaborate = args: let
+  elaborate = args': let
+    args = if lib.isString args' then { system = args'; }
+           else args';
     final = {
       # Prefer to parse `config` as it is strictly more informative.
       parsed = parse.mkSystemFromString (if args ? config then args.config else args.system);
@@ -24,15 +25,20 @@ rec {
       config = parse.tripleFromSystem final.parsed;
       # Just a guess, based on `system`
       platform = platforms.selectBySystem final.system;
+      # Determine whether we are compatible with the provided CPU
+      isCompatible = platform: parse.isCompatible final.parsed.cpu platform.parsed.cpu;
       # Derived meta-data
       libc =
         /**/ if final.isDarwin              then "libSystem"
         else if final.isMinGW               then "msvcrt"
+        else if final.isWasi                then "wasilibc"
         else if final.isMusl                then "musl"
         else if final.isUClibc              then "uclibc"
         else if final.isAndroid             then "bionic"
         else if final.isLinux /* default */ then "glibc"
+        else if final.isMsp430              then "newlib"
         else if final.isAvr                 then "avrlibc"
+        else if final.isNetBSD              then "nblibc"
         # TODO(@Ericson2314) think more about other operating systems
         else                                     "native/impure";
       extensions = {
@@ -58,7 +64,7 @@ rec {
           "netbsd" = "NetBSD";
           "freebsd" = "FreeBSD";
           "openbsd" = "OpenBSD";
-          "wasm" = "Wasm";
+          "wasi" = "Wasi";
         }.${final.parsed.kernel.name} or null;
 
          # uname -p
@@ -68,16 +74,22 @@ rec {
          release = null;
       };
 
+      kernelArch =
+        if final.isAarch32 then "arm"
+        else if final.isAarch64 then "arm64"
+        else if final.isx86_32 then "x86"
+        else if final.isx86_64 then "ia64"
+        else final.parsed.cpu.name;
+
       qemuArch =
         if final.isArm then "arm"
         else if final.isx86_64 then "x86_64"
         else if final.isx86 then "i386"
         else {
           "powerpc" = "ppc";
+          "powerpcle" = "ppc";
           "powerpc64" = "ppc64";
-          "powerpc64le" = "ppc64";
-          "mips64" = "mips";
-          "mipsel64" = "mipsel";
+          "powerpc64le" = "ppc64le";
         }.${final.parsed.cpu.name} or final.parsed.cpu.name;
 
       emulator = pkgs: let
@@ -98,13 +110,14 @@ rec {
         wine = (pkgs.winePackagesFor wine-name).minimal;
       in
         if final.parsed.kernel.name == pkgs.stdenv.hostPlatform.parsed.kernel.name &&
-           (final.parsed.cpu.name == pkgs.stdenv.hostPlatform.parsed.cpu.name ||
-            (final.isi686 && pkgs.stdenv.hostPlatform.isx86_64))
-        then pkgs.runtimeShell
+           pkgs.stdenv.hostPlatform.isCompatible final
+        then "${pkgs.runtimeShell} -c '\"$@\"' --"
         else if final.isWindows
         then "${wine}/bin/${wine-name}"
         else if final.isLinux && pkgs.stdenv.hostPlatform.isLinux
         then "${qemu-user}/bin/qemu-${final.qemuArch}"
+        else if final.isWasi
+        then "${pkgs.wasmtime}/bin/wasmtime"
         else throw "Don't know how to run ${final.config} executables.";
 
     } // mapAttrs (n: v: v final.parsed) inspect.predicates
diff --git a/lib/systems/doubles.nix b/lib/systems/doubles.nix
index 58677c0bdd900..823f6a915d6e7 100644
--- a/lib/systems/doubles.nix
+++ b/lib/systems/doubles.nix
@@ -13,10 +13,20 @@ let
 
     "i686-cygwin" "i686-freebsd" "i686-linux" "i686-netbsd" "i686-openbsd"
 
-    "x86_64-cygwin" "x86_64-darwin" "x86_64-freebsd" "x86_64-linux"
+    "x86_64-cygwin" "x86_64-freebsd" "x86_64-linux"
     "x86_64-netbsd" "x86_64-openbsd" "x86_64-solaris"
 
+    "x86_64-darwin" "i686-darwin" "aarch64-darwin" "armv7a-darwin"
+
     "x86_64-windows" "i686-windows"
+
+    "wasm64-wasi" "wasm32-wasi"
+
+    "powerpc64le-linux"
+
+    "riscv32-linux" "riscv64-linux"
+
+    "aarch64-none" "avr-none" "arm-none" "i686-none" "x86_64-none" "powerpc-none" "msp430-none" "riscv64-none" "riscv32-none"
   ];
 
   allParsed = map parse.mkSystemFromString all;
@@ -34,6 +44,7 @@ in rec {
   i686    = filterDoubles predicates.isi686;
   x86_64  = filterDoubles predicates.isx86_64;
   mips    = filterDoubles predicates.isMips;
+  riscv   = filterDoubles predicates.isRiscV;
 
   cygwin  = filterDoubles predicates.isCygwin;
   darwin  = filterDoubles predicates.isDarwin;
@@ -45,7 +56,10 @@ in rec {
   netbsd  = filterDoubles predicates.isNetBSD;
   openbsd = filterDoubles predicates.isOpenBSD;
   unix    = filterDoubles predicates.isUnix;
+  wasi    = filterDoubles predicates.isWasi;
   windows = filterDoubles predicates.isWindows;
 
-  mesaPlatforms = ["i686-linux" "x86_64-linux" "x86_64-darwin" "armv5tel-linux" "armv6l-linux" "armv7l-linux" "aarch64-linux" "powerpc64le-linux"];
+  embedded = filterDoubles predicates.isNone;
+
+  mesaPlatforms = ["i686-linux" "x86_64-linux" "x86_64-darwin" "armv5tel-linux" "armv6l-linux" "armv7l-linux" "armv7a-linux" "aarch64-linux" "powerpc64le-linux"];
 }
diff --git a/lib/systems/examples.nix b/lib/systems/examples.nix
index 90068f566ed08..4861fe634a02e 100644
--- a/lib/systems/examples.nix
+++ b/lib/systems/examples.nix
@@ -44,14 +44,6 @@ rec {
     platform = platforms.aarch64-multiplatform;
   };
 
-  armv5te-android-prebuilt = rec {
-    config = "armv5tel-unknown-linux-androideabi";
-    sdkVer = "21";
-    ndkVer = "18b";
-    platform = platforms.armv5te-android;
-    useAndroidPrebuilt = true;
-  };
-
   armv7a-android-prebuilt = rec {
     config = "armv7a-unknown-linux-androideabi";
     sdkVer = "24";
@@ -96,12 +88,32 @@ rec {
     config = "aarch64-unknown-linux-musl";
   };
 
+  gnu64 = { config = "x86_64-unknown-linux-gnu"; };
+  gnu32  = { config = "i686-unknown-linux-gnu"; };
+
   musl64 = { config = "x86_64-unknown-linux-musl"; };
   musl32  = { config = "i686-unknown-linux-musl"; };
 
   riscv64 = riscv "64";
   riscv32 = riscv "32";
 
+  riscv64-embedded = {
+    config = "riscv64-none-elf";
+    libc = "newlib";
+    platform = platforms.riscv-multiplatform "64";
+  };
+
+  riscv32-embedded = {
+    config = "riscv32-none-elf";
+    libc = "newlib";
+    platform = platforms.riscv-multiplatform "32";
+  };
+
+  msp430 = {
+    config = "msp430-elf";
+    libc = "newlib";
+  };
+
   avr = {
     config = "avr";
   };
@@ -135,11 +147,6 @@ rec {
     libc = "newlib";
   };
 
-  alpha-embedded = {
-    config = "alpha-elf";
-    libc = "newlib";
-  };
-
   i686-embedded = {
     config = "i686-elf";
     libc = "newlib";
@@ -213,6 +220,22 @@ rec {
     platform = {};
   };
 
+  # BSDs
+
+  amd64-netbsd = {
+    config = "x86_64-unknown-netbsd";
+    libc = "nblibc";
+  };
+
+  #
+  # WASM
+  #
+
+  wasi32 = {
+    config = "wasm32-unknown-wasi";
+    useLLVM = true;
+  };
+
   # Ghcjs
   ghcjs = {
     config = "js-unknown-ghcjs";
diff --git a/lib/systems/for-meta.nix b/lib/systems/for-meta.nix
deleted file mode 100644
index 51fb6ae760d1e..0000000000000
--- a/lib/systems/for-meta.nix
+++ /dev/null
@@ -1,37 +0,0 @@
-{ lib }:
-let
-  inherit (lib.systems) parse;
-  inherit (lib.systems.inspect) patterns;
-
-  abis = lib.mapAttrs (_: abi: builtins.removeAttrs abi [ "assertions" ]) parse.abis;
-
-in rec {
-  all     = [ {} ]; # `{}` matches anything
-  none    = [];
-
-  arm     = [ patterns.isAarch32 ];
-  aarch64 = [ patterns.isAarch64 ];
-  x86     = [ patterns.isx86 ];
-  i686    = [ patterns.isi686 ];
-  x86_64  = [ patterns.isx86_64 ];
-  mips    = [ patterns.isMips ];
-  riscv   = [ patterns.isRiscV ];
-
-  cygwin  = [ patterns.isCygwin ];
-  darwin  = [ patterns.isDarwin ];
-  freebsd = [ patterns.isFreeBSD ];
-  # Should be better, but MinGW is unclear.
-  gnu     = [
-    { kernel = parse.kernels.linux; abi = abis.gnu; }
-    { kernel = parse.kernels.linux; abi = abis.gnueabi; }
-    { kernel = parse.kernels.linux; abi = abis.gnueabihf; }
-  ];
-  illumos = [ patterns.isSunOS ];
-  linux   = [ patterns.isLinux ];
-  netbsd  = [ patterns.isNetBSD ];
-  openbsd = [ patterns.isOpenBSD ];
-  unix    = patterns.isUnix; # Actually a list
-  windows = [ patterns.isWindows ];
-
-  inherit (lib.systems.doubles) mesaPlatforms;
-}
diff --git a/lib/systems/inspect.nix b/lib/systems/inspect.nix
index bf186126d4a9b..8a95787110b82 100644
--- a/lib/systems/inspect.nix
+++ b/lib/systems/inspect.nix
@@ -20,7 +20,9 @@ rec {
     isRiscV        = { cpu = { family = "riscv"; }; };
     isSparc        = { cpu = { family = "sparc"; }; };
     isWasm         = { cpu = { family = "wasm"; }; };
+    isMsp430       = { cpu = { family = "msp430"; }; };
     isAvr          = { cpu = { family = "avr"; }; };
+    isAlpha        = { cpu = { family = "alpha"; }; };
 
     is32bit        = { cpu = { bits = 32; }; };
     is64bit        = { cpu = { bits = 64; }; };
@@ -41,6 +43,8 @@ rec {
     isWindows      = { kernel = kernels.windows; };
     isCygwin       = { kernel = kernels.windows; abi = abis.cygnus; };
     isMinGW        = { kernel = kernels.windows; abi = abis.gnu; };
+    isWasi         = { kernel = kernels.wasi; };
+    isNone         = { kernel = kernels.none; };
 
     isAndroid      = [ { abi = abis.android; } { abi = abis.androideabi; } ];
     isMusl         = with abis; map (a: { abi = a; }) [ musl musleabi musleabihf ];
diff --git a/lib/systems/parse.nix b/lib/systems/parse.nix
index a20b334311efa..0c42689a9b137 100644
--- a/lib/systems/parse.nix
+++ b/lib/systems/parse.nix
@@ -69,24 +69,24 @@ rec {
 
   cpuTypes = with significantBytes; setTypes types.openCpuType {
     arm      = { bits = 32; significantByte = littleEndian; family = "arm"; };
-    armv5tel = { bits = 32; significantByte = littleEndian; family = "arm"; version = "5"; };
-    armv6m   = { bits = 32; significantByte = littleEndian; family = "arm"; version = "6"; };
-    armv6l   = { bits = 32; significantByte = littleEndian; family = "arm"; version = "6"; };
-    armv7a   = { bits = 32; significantByte = littleEndian; family = "arm"; version = "7"; };
-    armv7r   = { bits = 32; significantByte = littleEndian; family = "arm"; version = "7"; };
-    armv7m   = { bits = 32; significantByte = littleEndian; family = "arm"; version = "7"; };
-    armv7l   = { bits = 32; significantByte = littleEndian; family = "arm"; version = "7"; };
-    armv8a   = { bits = 32; significantByte = littleEndian; family = "arm"; version = "8"; };
-    armv8r   = { bits = 32; significantByte = littleEndian; family = "arm"; version = "8"; };
-    armv8m   = { bits = 32; significantByte = littleEndian; family = "arm"; version = "8"; };
-    aarch64  = { bits = 64; significantByte = littleEndian; family = "arm"; version = "8"; };
-    aarch64_be = { bits = 64; significantByte = bigEndian; family = "arm"; version = "8"; };
-
-    i386     = { bits = 32; significantByte = littleEndian; family = "x86"; };
-    i486     = { bits = 32; significantByte = littleEndian; family = "x86"; };
-    i586     = { bits = 32; significantByte = littleEndian; family = "x86"; };
-    i686     = { bits = 32; significantByte = littleEndian; family = "x86"; };
-    x86_64   = { bits = 64; significantByte = littleEndian; family = "x86"; };
+    armv5tel = { bits = 32; significantByte = littleEndian; family = "arm"; version = "5"; arch = "armv5t"; };
+    armv6m   = { bits = 32; significantByte = littleEndian; family = "arm"; version = "6"; arch = "armv6-m"; };
+    armv6l   = { bits = 32; significantByte = littleEndian; family = "arm"; version = "6"; arch = "armv6"; };
+    armv7a   = { bits = 32; significantByte = littleEndian; family = "arm"; version = "7"; arch = "armv7-a"; };
+    armv7r   = { bits = 32; significantByte = littleEndian; family = "arm"; version = "7"; arch = "armv7-r"; };
+    armv7m   = { bits = 32; significantByte = littleEndian; family = "arm"; version = "7"; arch = "armv7-m"; };
+    armv7l   = { bits = 32; significantByte = littleEndian; family = "arm"; version = "7"; arch = "armv7"; };
+    armv8a   = { bits = 32; significantByte = littleEndian; family = "arm"; version = "8"; arch = "armv8-a"; };
+    armv8r   = { bits = 32; significantByte = littleEndian; family = "arm"; version = "8"; arch = "armv8-a"; };
+    armv8m   = { bits = 32; significantByte = littleEndian; family = "arm"; version = "8"; arch = "armv8-m"; };
+    aarch64  = { bits = 64; significantByte = littleEndian; family = "arm"; version = "8"; arch = "armv8-a"; };
+    aarch64_be = { bits = 64; significantByte = bigEndian; family = "arm"; version = "8";  arch = "armv8-a"; };
+
+    i386     = { bits = 32; significantByte = littleEndian; family = "x86"; arch = "i386"; };
+    i486     = { bits = 32; significantByte = littleEndian; family = "x86"; arch = "i486"; };
+    i586     = { bits = 32; significantByte = littleEndian; family = "x86"; arch = "i586"; };
+    i686     = { bits = 32; significantByte = littleEndian; family = "x86"; arch = "i686"; };
+    x86_64   = { bits = 64; significantByte = littleEndian; family = "x86"; arch = "x86-64"; };
 
     mips     = { bits = 32; significantByte = bigEndian;    family = "mips"; };
     mipsel   = { bits = 32; significantByte = littleEndian; family = "mips"; };
@@ -109,11 +109,92 @@ rec {
 
     alpha    = { bits = 64; significantByte = littleEndian; family = "alpha"; };
 
+    msp430   = { bits = 16; significantByte = littleEndian; family = "msp430"; };
     avr      = { bits = 8; family = "avr"; };
 
     js       = { bits = 32; significantByte = littleEndian; family = "js"; };
   };
 
+  # Determine where two CPUs are compatible with each other. That is,
+  # can we run code built for system b on system a? For that to
+  # happen, then the set of all possible possible programs that system
+  # b accepts must be a subset of the set of all programs that system
+  # a accepts. This compatibility relation forms a category where each
+  # CPU is an object and each arrow from a to b represents
+  # compatibility. CPUs with multiple modes of Endianness are
+  # isomorphic while all CPUs are endomorphic because any program
+  # built for a CPU can run on that CPU.
+  isCompatible = a: b: with cpuTypes; lib.any lib.id [
+    # x86
+    (b == i386 && isCompatible a i486)
+    (b == i486 && isCompatible a i586)
+    (b == i586 && isCompatible a i686)
+
+    # XXX: Not true in some cases. Like in WSL mode.
+    (b == i686 && isCompatible a x86_64)
+
+    # ARMv4
+    (b == arm && isCompatible a armv5tel)
+
+    # ARMv5
+    (b == armv5tel && isCompatible a armv6l)
+
+    # ARMv6
+    (b == armv6l && isCompatible a armv6m)
+    (b == armv6m && isCompatible a armv7l)
+
+    # ARMv7
+    (b == armv7l && isCompatible a armv7a)
+    (b == armv7l && isCompatible a armv7r)
+    (b == armv7l && isCompatible a armv7m)
+    (b == armv7a && isCompatible a armv8a)
+    (b == armv7r && isCompatible a armv8a)
+    (b == armv7m && isCompatible a armv8a)
+    (b == armv7a && isCompatible a armv8r)
+    (b == armv7r && isCompatible a armv8r)
+    (b == armv7m && isCompatible a armv8r)
+    (b == armv7a && isCompatible a armv8m)
+    (b == armv7r && isCompatible a armv8m)
+    (b == armv7m && isCompatible a armv8m)
+
+    # ARMv8
+    (b == armv8r && isCompatible a armv8a)
+    (b == armv8m && isCompatible a armv8a)
+
+    # XXX: not always true! Some arm64 cpus don’t support arm32 mode.
+    (b == aarch64 && a == armv8a)
+    (b == armv8a && isCompatible a aarch64)
+
+    (b == aarch64 && a == aarch64_be)
+    (b == aarch64_be && isCompatible a aarch64)
+
+    # PowerPC
+    (b == powerpc && isCompatible a powerpc64)
+    (b == powerpcle && isCompatible a powerpc)
+    (b == powerpc && a == powerpcle)
+    (b == powerpc64le && isCompatible a powerpc64)
+    (b == powerpc64 && a == powerpc64le)
+
+    # MIPS
+    (b == mips && isCompatible a mips64)
+    (b == mips && a == mipsel)
+    (b == mipsel && isCompatible a mips)
+    (b == mips64 && a == mips64el)
+    (b == mips64el && isCompatible a mips64)
+
+    # RISCV
+    (b == riscv32 && isCompatible a riscv64)
+
+    # SPARC
+    (b == sparc && isCompatible a sparc64)
+
+    # WASM
+    (b == wasm32 && isCompatible a wasm64)
+
+    # identity
+    (b == a)
+  ];
+
   ################################################################################
 
   types.openVendor = mkOptionType {
@@ -147,6 +228,7 @@ rec {
     elf = {};
     macho = {};
     pe = {};
+    wasm = {};
 
     unknown = {};
   };
@@ -189,6 +271,7 @@ rec {
     none    = { execFormat = unknown; families = { }; };
     openbsd = { execFormat = elf;     families = { inherit bsd; }; };
     solaris = { execFormat = elf;     families = { }; };
+    wasi    = { execFormat = wasm;    families = { }; };
     windows = { execFormat = pe;      families = { }; };
     ghcjs   = { execFormat = unknown; families = { }; };
   } // { # aliases
@@ -298,6 +381,8 @@ rec {
         then { cpu = elemAt l 0;                      kernel = elemAt l 1; abi = elemAt l 2; }
       else if (elemAt l 2 == "mingw32") # autotools breaks on -gnu for window
         then { cpu = elemAt l 0; vendor = elemAt l 1; kernel = "windows";                    }
+      else if (elemAt l 2 == "wasi")
+        then { cpu = elemAt l 0; vendor = elemAt l 1; kernel = "wasi";                       }
       else if hasPrefix "netbsd" (elemAt l 2)
         then { cpu = elemAt l 0; vendor = elemAt l 1;    kernel = elemAt l 2;                }
       else if (elem (elemAt l 2) ["eabi" "eabihf" "elf"])
@@ -348,7 +433,7 @@ rec {
 
   mkSystemFromString = s: mkSystemFromSkeleton (mkSkeletonFromList (lib.splitString "-" s));
 
-  doubleFromSystem = { cpu, vendor, kernel, abi, ... }:
+  doubleFromSystem = { cpu, kernel, abi, ... }:
     /**/ if abi == abis.cygnus       then "${cpu.name}-cygwin"
     else if kernel.families ? darwin then "${cpu.name}-darwin"
     else "${cpu.name}-${kernel.name}";
diff --git a/lib/systems/platforms.nix b/lib/systems/platforms.nix
index 03bfce256103c..a2b43c970a411 100644
--- a/lib/systems/platforms.nix
+++ b/lib/systems/platforms.nix
@@ -253,22 +253,11 @@ rec {
     kernelTarget = "zImage";
   };
 
-  # https://developer.android.com/ndk/guides/abis#armeabi
-  armv5te-android = {
-    name = "armeabi";
-    gcc = {
-      arch = "armv5te";
-      float = "soft";
-      float-abi = "soft";
-    };
-  };
-
   # https://developer.android.com/ndk/guides/abis#v7a
   armv7a-android =  {
     name = "armeabi-v7a";
     gcc = {
       arch = "armv7-a";
-      float = "hard";
       float-abi = "softfp";
       fpu = "vfpv3-d16";
     };
diff --git a/lib/tests/modules.sh b/lib/tests/modules.sh
index a72777cbf2a68..cf344122cf4e4 100755
--- a/lib/tests/modules.sh
+++ b/lib/tests/modules.sh
@@ -71,6 +71,15 @@ checkConfigError 'The option value .* in .* is not of type.*positive integer.*'
 checkConfigOutput "42" config.value ./declare-int-between-value.nix ./define-value-int-positive.nix
 checkConfigError 'The option value .* in .* is not of type.*between.*-21 and 43.*inclusive.*' config.value ./declare-int-between-value.nix ./define-value-int-negative.nix
 
+# Check either types
+# types.either
+checkConfigOutput "42" config.value ./declare-either.nix ./define-value-int-positive.nix
+checkConfigOutput "\"24\"" config.value ./declare-either.nix ./define-value-string.nix
+# types.oneOf
+checkConfigOutput "42" config.value ./declare-oneOf.nix ./define-value-int-positive.nix
+checkConfigOutput "[ ]" config.value ./declare-oneOf.nix ./define-value-list.nix
+checkConfigOutput "\"24\"" config.value ./declare-oneOf.nix ./define-value-string.nix
+
 # Check mkForce without submodules.
 set -- config.enable ./declare-enable.nix ./define-enable.nix
 checkConfigOutput "true" "$@"
@@ -149,7 +158,7 @@ checkConfigOutput "1 2 3 4 5 6 7 8 9 10" config.result ./loaOf-with-long-list.ni
 # Check loaOf with many merges of lists.
 checkConfigOutput "1 2 3 4 5 6 7 8 9 10" config.result ./loaOf-with-many-list-merges.nix
 
-# Check mkAliasOptionModuleWithPriority.
+# Check mkAliasOptionModule.
 checkConfigOutput "true" config.enable ./alias-with-priority.nix
 checkConfigOutput "true" config.enableAlias ./alias-with-priority.nix
 checkConfigOutput "false" config.enable ./alias-with-priority-can-override.nix
diff --git a/lib/tests/modules/alias-with-priority-can-override.nix b/lib/tests/modules/alias-with-priority-can-override.nix
index a6b26895f3a8d..9a18c9d9f613b 100644
--- a/lib/tests/modules/alias-with-priority-can-override.nix
+++ b/lib/tests/modules/alias-with-priority-can-override.nix
@@ -1,5 +1,8 @@
 # This is a test to show that mkAliasOptionModule sets the priority correctly
 # for aliased options.
+#
+# This test shows that an alias with a high priority is able to override
+# a non-aliased option.
 
 { config, lib, ... }:
 
@@ -32,10 +35,10 @@ with lib;
 
   imports = [
     # Create an alias for the "enable" option.
-    (mkAliasOptionModuleWithPriority [ "enableAlias" ] [ "enable" ])
+    (mkAliasOptionModule [ "enableAlias" ] [ "enable" ])
 
-    # Disable the aliased option, but with a default (low) priority so it
-    # should be able to be overridden by the next import.
+    # Disable the aliased option with a high priority so it
+    # should override the next import.
     ( { config, lib, ... }:
       {
         enableAlias = lib.mkForce false;
diff --git a/lib/tests/modules/alias-with-priority.nix b/lib/tests/modules/alias-with-priority.nix
index 923483684cb16..a35a06fc69749 100644
--- a/lib/tests/modules/alias-with-priority.nix
+++ b/lib/tests/modules/alias-with-priority.nix
@@ -1,5 +1,8 @@
 # This is a test to show that mkAliasOptionModule sets the priority correctly
 # for aliased options.
+#
+# This test shows that an alias with a low priority is able to be overridden
+# with a non-aliased option.
 
 { config, lib, ... }:
 
@@ -32,7 +35,7 @@ with lib;
 
   imports = [
     # Create an alias for the "enable" option.
-    (mkAliasOptionModuleWithPriority [ "enableAlias" ] [ "enable" ])
+    (mkAliasOptionModule [ "enableAlias" ] [ "enable" ])
 
     # Disable the aliased option, but with a default (low) priority so it
     # should be able to be overridden by the next import.
diff --git a/lib/tests/modules/declare-either.nix b/lib/tests/modules/declare-either.nix
new file mode 100644
index 0000000000000..5a0fa978a1381
--- /dev/null
+++ b/lib/tests/modules/declare-either.nix
@@ -0,0 +1,5 @@
+{ lib, ... }: {
+  options.value = lib.mkOption {
+    type = lib.types.either lib.types.int lib.types.str;
+  };
+}
diff --git a/lib/tests/modules/declare-oneOf.nix b/lib/tests/modules/declare-oneOf.nix
new file mode 100644
index 0000000000000..df092a14f81e2
--- /dev/null
+++ b/lib/tests/modules/declare-oneOf.nix
@@ -0,0 +1,9 @@
+{ lib, ... }: {
+  options.value = lib.mkOption {
+    type = lib.types.oneOf [
+      lib.types.int
+      (lib.types.listOf lib.types.int)
+      lib.types.str
+    ];
+  };
+}
diff --git a/lib/tests/release.nix b/lib/tests/release.nix
index d9a8a00672538..737d142d25324 100644
--- a/lib/tests/release.nix
+++ b/lib/tests/release.nix
@@ -1,11 +1,9 @@
 { pkgs ? import ((import ../.).cleanSource ../..) {} }:
 
-pkgs.stdenv.mkDerivation {
-  name = "nixpkgs-lib-tests";
-  buildInputs = [ pkgs.nix ];
+pkgs.runCommandNoCC "nixpkgs-lib-tests" {
+  buildInputs = [ pkgs.nix (import ./check-eval.nix) ];
   NIX_PATH="nixpkgs=${pkgs.path}";
-
-  buildCommand = ''
+} ''
     datadir="${pkgs.nix}/share"
     export TEST_ROOT=$(pwd)/test-tmp
     export NIX_BUILD_HOOK=
@@ -22,10 +20,5 @@ pkgs.stdenv.mkDerivation {
     cd ${pkgs.path}/lib/tests
     bash ./modules.sh
 
-    [[ "$(nix-instantiate --eval --strict misc.nix)" == "[ ]" ]]
-
-    [[ "$(nix-instantiate --eval --strict systems.nix)" == "[ ]" ]]
-
     touch $out
-  '';
-}
+''
diff --git a/lib/tests/systems.nix b/lib/tests/systems.nix
index 5e12936582159..5748c09b564d5 100644
--- a/lib/tests/systems.nix
+++ b/lib/tests/systems.nix
@@ -12,19 +12,19 @@ let
     expected = lib.sort lib.lessThan y;
   };
 in with lib.systems.doubles; lib.runTests {
-  testall = mseteq all (linux ++ darwin ++ freebsd ++ openbsd ++ netbsd ++ illumos ++ windows);
+  testall = mseteq all (linux ++ darwin ++ freebsd ++ openbsd ++ netbsd ++ illumos ++ wasi ++ windows ++ embedded);
 
-  testarm = mseteq arm [ "armv5tel-linux" "armv6l-linux" "armv7l-linux" ];
-  testi686 = mseteq i686 [ "i686-linux" "i686-freebsd" "i686-netbsd" "i686-openbsd" "i686-cygwin" "i686-windows" ];
+  testarm = mseteq arm [ "armv5tel-linux" "armv6l-linux" "armv7l-linux" "arm-none" "armv7a-darwin" ];
+  testi686 = mseteq i686 [ "i686-linux" "i686-freebsd" "i686-netbsd" "i686-openbsd" "i686-cygwin" "i686-windows" "i686-none" "i686-darwin" ];
   testmips = mseteq mips [ "mipsel-linux" ];
-  testx86_64 = mseteq x86_64 [ "x86_64-linux" "x86_64-darwin" "x86_64-freebsd" "x86_64-openbsd" "x86_64-netbsd" "x86_64-cygwin" "x86_64-solaris" "x86_64-windows" ];
+  testx86_64 = mseteq x86_64 [ "x86_64-linux" "x86_64-darwin" "x86_64-freebsd" "x86_64-openbsd" "x86_64-netbsd" "x86_64-cygwin" "x86_64-solaris" "x86_64-windows" "x86_64-none" ];
 
   testcygwin = mseteq cygwin [ "i686-cygwin" "x86_64-cygwin" ];
-  testdarwin = mseteq darwin [ "x86_64-darwin" ];
+  testdarwin = mseteq darwin [ "x86_64-darwin" "i686-darwin" "aarch64-darwin" "armv7a-darwin" ];
   testfreebsd = mseteq freebsd [ "i686-freebsd" "x86_64-freebsd" ];
   testgnu = mseteq gnu (linux /* ++ kfreebsd ++ ... */);
   testillumos = mseteq illumos [ "x86_64-solaris" ];
-  testlinux = mseteq linux [ "i686-linux" "x86_64-linux" "armv5tel-linux" "armv6l-linux" "armv7l-linux" "aarch64-linux" "mipsel-linux" ];
+  testlinux = mseteq linux [ "aarch64-linux" "armv5tel-linux" "armv6l-linux" "armv7l-linux" "i686-linux" "mipsel-linux" "riscv32-linux" "riscv64-linux" "x86_64-linux" "powerpc64le-linux" ];
   testnetbsd = mseteq netbsd [ "i686-netbsd" "x86_64-netbsd" ];
   testopenbsd = mseteq openbsd [ "i686-openbsd" "x86_64-openbsd" ];
   testwindows = mseteq windows [ "i686-cygwin" "x86_64-cygwin" "i686-windows" "x86_64-windows" ];
diff --git a/lib/trivial.nix b/lib/trivial.nix
index 17489311236d0..f2710a6f0338d 100644
--- a/lib/trivial.nix
+++ b/lib/trivial.nix
@@ -112,7 +112,7 @@ rec {
     # Function to call
     f:
     # Argument to check for null before passing it to `f`
-    a: if isNull a then a else f a;
+    a: if a == null then a else f a;
 
   # Pull in some builtins not included elsewhere.
   inherit (builtins)
@@ -134,7 +134,7 @@ rec {
      On each release the first letter is bumped and a new animal is chosen
      starting with that new letter.
   */
-  codeName = "Koi";
+  codeName = "Loris";
 
   /* Returns the current nixpkgs version suffix as string. */
   versionSuffix =
@@ -259,9 +259,10 @@ rec {
   # TODO: figure out a clever way to integrate location information from
   # something like __unsafeGetAttrPos.
 
-  warn = msg: builtins.trace "WARNING: ${msg}";
+  warn = msg: builtins.trace "warning: ${msg}";
   info = msg: builtins.trace "INFO: ${msg}";
 
+  showWarnings = warnings: res: lib.fold (w: x: warn w x) res warnings;
 
   ## Function annotations
 
diff --git a/lib/types.nix b/lib/types.nix
index 7a88e1b9e36be..bcb5de0c379b8 100644
--- a/lib/types.nix
+++ b/lib/types.nix
@@ -111,7 +111,7 @@ rec {
         name = "int";
         description = "signed integer";
         check = isInt;
-        merge = mergeOneOption;
+        merge = mergeEqualOption;
       };
 
     # Specialized subdomains of int
@@ -176,14 +176,14 @@ rec {
         name = "float";
         description = "floating point number";
         check = isFloat;
-        merge = mergeOneOption;
+        merge = mergeEqualOption;
     };
 
     str = mkOptionType {
       name = "str";
       description = "string";
       check = isString;
-      merge = mergeOneOption;
+      merge = mergeEqualOption;
     };
 
     strMatching = pattern: mkOptionType {
@@ -217,7 +217,8 @@ rec {
 
     # Deprecated; should not be used because it quietly concatenates
     # strings, which is usually not what you want.
-    string = separatedString "";
+    string = warn "types.string is deprecated because it quietly concatenates strings"
+      (separatedString "");
 
     attrs = mkOptionType {
       name = "attrs";
@@ -243,7 +244,7 @@ rec {
       name = "path";
       # Hacky: there is no ‘isPath’ primop.
       check = x: builtins.substring 0 1 (toString x) == "/";
-      merge = mergeOneOption;
+      merge = mergeEqualOption;
     };
 
     # drop this in the future:
@@ -415,7 +416,7 @@ rec {
         name = "enum";
         description = "one of ${concatMapStringsSep ", " show values}";
         check = flip elem values;
-        merge = mergeOneOption;
+        merge = mergeEqualOption;
         functor = (defaultFunctor name) // { payload = values; binOp = a: b: unique (a ++ b); };
       };
 
@@ -443,6 +444,13 @@ rec {
       functor = (defaultFunctor name) // { wrapped = [ t1 t2 ]; };
     };
 
+    # Any of the types in the given list
+    oneOf = ts:
+      let
+        head' = if ts == [] then throw "types.oneOf needs to get at least one type in its argument" else head ts;
+        tail' = tail ts;
+      in foldl' either head' tail';
+
     # Either value of type `finalType` or `coercedType`, the latter is
     # converted to `finalType` using `coerceFunc`.
     coercedTo = coercedType: coerceFunc: finalType:
@@ -469,8 +477,10 @@ rec {
     # Obsolete alternative to configOf.  It takes its option
     # declarations from the ‘options’ attribute of containing option
     # declaration.
-    optionSet = builtins.throw "types.optionSet is deprecated; use types.submodule instead" "optionSet";
-
+    optionSet = mkOptionType {
+      name = builtins.trace "types.optionSet is deprecated; use types.submodule instead" "optionSet";
+      description = "option set";
+    };
     # Augment the given type with an additional type check function.
     addCheck = elemType: check: elemType // { check = x: elemType.check x && check x; };