summary refs log tree commit diff
path: root/lib
diff options
context:
space:
mode:
authorAdam Joseph <adam@westernsemico.com>2022-09-25 00:09:13 -0700
committerAdam Joseph <adam@westernsemico.com>2022-09-25 00:09:15 -0700
commit037cf2fad190766319c6c40b931b49c075ce5e78 (patch)
tree8e6ed01234d418241fc7c52c5e3fcdcf8b5eced0 /lib
parent99da19387705b90647741f020ad2f835e6c8056b (diff)
unionOfDisjoint: use builtins.intersectAttrs
This brings two benefits:

1. The complete list of collisions is printed in the whenever any colliding
   attribute is accessed.

2. The sets are intersected using a C++ primitive, which runs in O(n) time
   (intersecting pre-sorted lists) with small constants rather than interpreted
   Nix code.

Thanks to @toonn for prompting this improvement.
Diffstat (limited to 'lib')
-rw-r--r--lib/attrsets.nix13
1 files changed, 8 insertions, 5 deletions
diff --git a/lib/attrsets.nix b/lib/attrsets.nix
index 31dc27969cc04..de88763854d69 100644
--- a/lib/attrsets.nix
+++ b/lib/attrsets.nix
@@ -627,11 +627,14 @@ rec {
      `y`, and all values `assert` with an error message.  This
       operator is commutative, unlike (//). */
   unionOfDisjoint = x: y:
-    x // (mapAttrs
-      (name: val:
-        if hasAttr name x
-        then builtins.throw "attribute collision: ${name}"
-        else val) y);
+    let
+      intersection = builtins.intersectAttrs x y;
+      collisions = lib.concatStringsSep " " (builtins.attrNames intersection);
+      mask = builtins.mapAttrs (name: value: builtins.throw
+        "unionOfDisjoint: collision on ${name}; complete list: ${collisions}")
+        intersection;
+    in
+      (x // y) // mask;
 
   /*** deprecated stuff ***/