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.
This commit is contained in:
Adam Joseph 2022-09-25 00:09:13 -07:00
parent 99da193877
commit 037cf2fad1

View File

@ -627,11 +627,14 @@ rec {
`y`, and all values `assert` with an error message. This `y`, and all values `assert` with an error message. This
operator is commutative, unlike (//). */ operator is commutative, unlike (//). */
unionOfDisjoint = x: y: unionOfDisjoint = x: y:
x // (mapAttrs let
(name: val: intersection = builtins.intersectAttrs x y;
if hasAttr name x collisions = lib.concatStringsSep " " (builtins.attrNames intersection);
then builtins.throw "attribute collision: ${name}" mask = builtins.mapAttrs (name: value: builtins.throw
else val) y); "unionOfDisjoint: collision on ${name}; complete list: ${collisions}")
intersection;
in
(x // y) // mask;
/*** deprecated stuff ***/ /*** deprecated stuff ***/