Accessing custom lib attributes reaches deeper than intended

Hello!

Sorry; I don’t know how to word the title correctly. I have a custom lib module here:

with builtins; { lib, extras ? {} }: with lib; let
    newLib = self: recursiveUpdate extras (rec {
        foldToSet = list: foldr (new: old: recursiveUpdate new old) {} (filter isAttrs list);
        foldToSet' = list: foldr (new: old: new // old) {} (filter isAttrs list);
        mif = {
            list = condition: value: optionals condition value;
            list' = condition: value: optional condition value;
            set = condition: value: optionalAttrs condition value;
            num = condition: value: if condition then value else 0;
            null = condition: value: if condition then value else null;
            str = condition: value: optionalString condition value;
        };
        dirCon = let
            ord = dir: func: filterAttrs func (if (isAttrs dir) then dir else (readDirExists dir));
        in rec {
            attrs = {
                dirs = dir: ord dir (n: v: v == "directory");
                others = dir: ord dir (n: v: v != "directory");
                files = dir: ord dir (n: v: v == "regular");
                sym = dir: ord dir (n: v: v == "symlink");
                unknown = dir: ord dir (n: v: v == "unknown");
            };
            dirs = dir: attrNames (attrs.dirs dir);
            others = dir: attrNames (attrs.others dir);
            files = dir: attrNames (attrs.files dir);
            sym = dir: attrNames (attrs.sym dir);
            unknown = dir: attrNames (attrs.unknown dir);
        };
        attrs = rec {
            configs = {
                nixpkgs = {
                    allowUnfree = true;
                    allowBroken = true;
                    allowUnsupportedSystem = true;
                    # preBuild = ''
                    #     makeFlagsArray+=(CFLAGS="-w")
                    #     buildFlagsArray+=(CC=cc)
                    # '';
                    permittedInsecurePackages = [
                        "python2.7-cryptography-2.9.2"
                    ];
                };
            };
            platforms = {
                arm = [ "aarch64-linux" "armv7l-linux" "armv6l-linux" ];
                imd = [ "i686-linux" "x86_64-linux" ];
            };
            versions = {
                python = rec {
                    two' = "7";
                    three' = "10";
                    two = "python2${two'}";
                    three = "python3${three'}";
                };
            };
        };
    });
    extension = makeExtensible newLib;
in with lib; extension.extend (final: prev: extension.foldToSet (attrValues prev))

Which is called from lib.nix using package.nix:

nixpkgs: nixpkgs.lib.extend (final: prev: { j = import ./lib.nix { lib = final; }; })

Which is finally defined in this flake:

{
    inputs = {
        nixpkgs.url = github:NixOS/nixpkgs/nixos-22.05;
        flake-utils.url = github:numtide/flake-utils;
    };
    outputs = inputs@{ self, nixpkgs, flake-utils, ... }: with builtins; with flake-utils.lib; let
        lib = import ./package.nix nixpkgs;
        overlays = with lib; rec {
            overlays = j.foldToSet' [
                {
                    lib = import ./overlay.nix nixpkgs;
                    default = overlays.lib;
                    "${trace (trace j j.attrs) j.attrs.versions.python.two}" = genAttrs (j.imports.list { dir = ./callPackages/python2; })
                                                                (file: final: prev: j.update.python.callPython.two final prev file);
                }
            ];
        };
        make = system: with lib; rec {
            legacyPackages = import nixpkgs { inherit system; overlays = attrValues overlays.overlays; };
            packages = flattenTree (genAttrs (attrNames overlays) (pkg: legacyPackages.${pkg}));
            package = packages.default;
            defaultPackage = package;
        };
    in eachSystem allSystems make;
}

The problem is that in trying to access j.attrs, j.dirCon.attrs is being accessed instead:

trace: { __unfix__ = <LAMBDA>; attrs = <CODE>; configs = <CODE>; dirCon = { attrs = <CODE>; dirs = <CODE>; files = <CODE>; others = <CODE>; sym = <CODE>; unknown = <CODE>; }; dirs = <CODE>; extend = <CODE>; files = <CODE>; foldToSet = <LAMBDA>; foldToSet' = <LAMBDA>; list = <CODE>; list' = <CODE>; mif = { list = <CODE>; list' = <CODE>; null = <CODE>; num = <CODE>; set = <CODE>; str = <CODE>; }; null = <CODE>; num = <CODE>; others = <CODE>; platforms = <CODE>; set = <CODE>; str = <CODE>; sym = <CODE>; unknown = <CODE>; versions = <CODE>; }
trace: { dirs = <CODE>; files = <CODE>; others = <CODE>; sym = <CODE>; unknown = <CODE>; }
error: attribute 'versions' missing

       at /nix/store/z8rv515r1b53i4kpjrhjlh3kr37xzkf2-source/flake.nix:13:48:

           12|                     default = overlays.lib;
           13|                     "${trace (trace j j.attrs) j.attrs.versions.python.two}" = genAttrs (j.imports.list { dir = ./callPackages/python2; })
             |                                                ^
           14|                                                                 (file: final: prev: j.update.python.callPython.two final prev file);

       … while evaluating the attribute 'attrs.versions.python.two'

       at /nix/store/dycwkjqxlc93s7ch3c932d0f9xvfch33-source/lib/attrsets.nix:466:7:

          465|       inherit name;
          466|       value = f name (catAttrs name sets);
             |       ^
          467|     }) names);

       … while evaluating 'fold''

       at /nix/store/dycwkjqxlc93s7ch3c932d0f9xvfch33-source/lib/lists.nix:56:15:

           55|       len = length list;
           56|       fold' = n:
             |               ^
           57|         if n == len

       … from call site

       at /nix/store/dycwkjqxlc93s7ch3c932d0f9xvfch33-source/lib/lists.nix:60:8:

           59|         else op (elemAt list n) (fold' (n + 1));
           60|     in fold' 0;
             |        ^
           61|

       … while evaluating 'foldr'

       at /nix/store/dycwkjqxlc93s7ch3c932d0f9xvfch33-source/lib/lists.nix:53:20:

           52|   */
           53|   foldr = op: nul: list:
             |                    ^
           54|     let

       … from call site

       at /nix/store/z8rv515r1b53i4kpjrhjlh3kr37xzkf2-source/lib.nix:4:28:

            3|         foldToSet = list: foldr (new: old: recursiveUpdate new old) {} (filter isAttrs list);
            4|         foldToSet' = list: foldr (new: old: new // old) {} (filter isAttrs list);
             |                            ^
            5|         mif = {

       … while evaluating 'foldToSet''

       at /nix/store/z8rv515r1b53i4kpjrhjlh3kr37xzkf2-source/lib.nix:4:22:

            3|         foldToSet = list: foldr (new: old: recursiveUpdate new old) {} (filter isAttrs list);
            4|         foldToSet' = list: foldr (new: old: new // old) {} (filter isAttrs list);
             |                      ^
            5|         mif = {

       … from call site

       at /nix/store/z8rv515r1b53i4kpjrhjlh3kr37xzkf2-source/flake.nix:9:24:

            8|         overlays = with lib; rec {
            9|             overlays = j.foldToSet' [
             |                        ^
           10|                 {

       … while evaluating the attribute 'overlays'

       at /nix/store/z8rv515r1b53i4kpjrhjlh3kr37xzkf2-source/flake.nix:9:13:

            8|         overlays = with lib; rec {
            9|             overlays = j.foldToSet' [
             |             ^
           10|                 {

       … while evaluating 'throwIfNot'

       at /nix/store/dycwkjqxlc93s7ch3c932d0f9xvfch33-source/lib/trivial.nix:379:22:

          378|   */
          379|   throwIfNot = cond: msg: if cond then x: x else throw msg;
             |                      ^
          380|

       … from call site

       at /nix/store/dycwkjqxlc93s7ch3c932d0f9xvfch33-source/pkgs/top-level/default.nix:55:5:

           54|   checked =
           55|     throwIfNot (lib.isList overlays) "The overlays argument to nixpkgs must be a list."
             |     ^
           56|     lib.foldr (x: throwIfNot (lib.isFunction x) "All overlays passed to nixpkgs must be functions.") (r: r) overlays

       … while evaluating anonymous lambda

       at /nix/store/dycwkjqxlc93s7ch3c932d0f9xvfch33-source/pkgs/top-level/default.nix:19:1:

           18|
           19| { # The system packages will be built on. See the manual for the
             | ^
           20|   # subtle division of labor between these two `*System`s and the three

       … from call site

       at /nix/store/dycwkjqxlc93s7ch3c932d0f9xvfch33-source/pkgs/top-level/impure.nix:82:1:

           81|
           82| import ./. (builtins.removeAttrs args [ "system" ] // {
             | ^
           83|   inherit config overlays localSystem;

       … while evaluating anonymous lambda

       at /nix/store/dycwkjqxlc93s7ch3c932d0f9xvfch33-source/pkgs/top-level/impure.nix:14:1:

           13|
           14| { # We put legacy `system` into `localSystem`, if `localSystem` was not passed.
             | ^
           15|   # If neither is passed, assume we are building packages on the current

       … from call site

       at /nix/store/z8rv515r1b53i4kpjrhjlh3kr37xzkf2-source/flake.nix:19:30:

           18|         make = system: with lib; rec {
           19|             legacyPackages = import nixpkgs { inherit system; overlays = attrValues overlays.overlays; };
             |                              ^
           20|             packages = flattenTree (genAttrs (attrNames overlays) (pkg: legacyPackages.${pkg}));

       … while evaluating anonymous lambda

       at /nix/store/z8rv515r1b53i4kpjrhjlh3kr37xzkf2-source/flake.nix:20:68:

           19|             legacyPackages = import nixpkgs { inherit system; overlays = attrValues overlays.overlays; };
           20|             packages = flattenTree (genAttrs (attrNames overlays) (pkg: legacyPackages.${pkg}));
             |                                                                    ^
           21|             package = packages.default;

       … from call site

       at /nix/store/dycwkjqxlc93s7ch3c932d0f9xvfch33-source/lib/attrsets.nix:414:43:

          413|   genAttrs = names: f:
          414|     listToAttrs (map (n: nameValuePair n (f n)) names);
             |                                           ^
          415|

       … while evaluating the attribute 'overlays'

       at /nix/store/dycwkjqxlc93s7ch3c932d0f9xvfch33-source/lib/attrsets.nix:312:41:

          311|   */
          312|   nameValuePair = name: value: { inherit name value; };
             |                                         ^
          313|

       … while evaluating 'op'

       at /nix/store/hiy5ba45bw71ls4qrgsfz2rxx7h83x52-source/flattenTree.nix:3:19:

            2| let
            3|   op = sum: path: val:
             |                   ^
            4|     let

       … from call site

       at /nix/store/hiy5ba45bw71ls4qrgsfz2rxx7h83x52-source/flattenTree.nix:30:18:

           29|     builtins.foldl'
           30|       (sum: key: op sum (path ++ [ key ]) val.${key})
             |                  ^
           31|       sum

       … while evaluating anonymous lambda

       at /nix/store/hiy5ba45bw71ls4qrgsfz2rxx7h83x52-source/flattenTree.nix:30:13:

           29|     builtins.foldl'
           30|       (sum: key: op sum (path ++ [ key ]) val.${key})
             |             ^
           31|       sum

       … from call site

       at /nix/store/hiy5ba45bw71ls4qrgsfz2rxx7h83x52-source/flattenTree.nix:29:5:

           28|   recurse = sum: path: val:
           29|     builtins.foldl'
             |     ^
           30|       (sum: key: op sum (path ++ [ key ]) val.${key})

       … while evaluating 'recurse'

       at /nix/store/hiy5ba45bw71ls4qrgsfz2rxx7h83x52-source/flattenTree.nix:28:24:

           27|
           28|   recurse = sum: path: val:
             |                        ^
           29|     builtins.foldl'

       … from call site

       at /nix/store/hiy5ba45bw71ls4qrgsfz2rxx7h83x52-source/flattenTree.nix:35:1:

           34| in
           35| recurse { } [ ] tree
             | ^

       … while evaluating anonymous lambda

       at /nix/store/hiy5ba45bw71ls4qrgsfz2rxx7h83x52-source/flattenTree.nix:1:1:

            1| tree:
             | ^
            2| let

       … from call site

       at /nix/store/hiy5ba45bw71ls4qrgsfz2rxx7h83x52-source/default.nix:172:23:

          171|   #   }
          172|   flattenTree = tree: import ./flattenTree.nix tree;
             |                       ^
          173|

       … while evaluating 'flattenTree'

       at /nix/store/hiy5ba45bw71ls4qrgsfz2rxx7h83x52-source/default.nix:172:17:

          171|   #   }
          172|   flattenTree = tree: import ./flattenTree.nix tree;
             |                 ^
          173|

       … from call site

       at /nix/store/z8rv515r1b53i4kpjrhjlh3kr37xzkf2-source/flake.nix:20:24:

           19|             legacyPackages = import nixpkgs { inherit system; overlays = attrValues overlays.overlays; };
           20|             packages = flattenTree (genAttrs (attrNames overlays) (pkg: legacyPackages.${pkg}));
             |                        ^
           21|             package = packages.default;

       … while evaluating the attribute 'packages'

       at /nix/store/z8rv515r1b53i4kpjrhjlh3kr37xzkf2-source/flake.nix:20:13:

           19|             legacyPackages = import nixpkgs { inherit system; overlays = attrValues overlays.overlays; };
           20|             packages = flattenTree (genAttrs (attrNames overlays) (pkg: legacyPackages.${pkg}));
             |             ^
           21|             package = packages.default;

I have no idea what’s going on, and any help would be greatly appreciated!

Solved it. I always thought extension.extend (final: prev: extension.foldToSet (attrValues prev)) looked funny; it was folding the inner sets back into the parent.