summary refs log tree commit diff
path: root/lib/generators.nix
diff options
context:
space:
mode:
authorMaximilian Bosch <maximilian@mbosch.me>2022-04-12 12:26:25 +0200
committerMaximilian Bosch <maximilian@mbosch.me>2022-04-12 12:34:23 +0200
commit72037880688ff9c6f4188a5746c3ab5ce7215628 (patch)
tree6d4f84448840b119ca880ef6b367f8adcd9e5b1a /lib/generators.nix
parentdbe672e4a718e400efa4ae71a9c91cc0739feb76 (diff)
lib/generators: withRecursion: don't break attr-sets with special attrs
Closes #168327

The issue reported there can be demonstrated with the following
expression:

    → nix-instantiate --eval -E "with import ./. {}; pkgs.lib.options.showDefs [ { file = \"foo\"; value = pkgs.rust.packages.stable.buildRustPackages; } ]"
    error: attempt to call something which is not a function but a string

           at /home/ma27/Projects/nixpkgs/lib/trivial.nix:442:35:

              441|   isFunction = f: builtins.isFunction f ||
              442|     (f ? __functor && isFunction (f.__functor f));
                 |                                   ^
              443|

Basically, if a `__functor` is in an attribute-set at depth-limit,
`__functor` will be set to `"<unevaluated>"`. This however breaks
`lib.isFunction` which checks for a `__functor` by invoking `__functor`
with `f` itself.

The same issue - "magic" attributes being shadowed by `withRecursion` -
also applies to others such as
`__pretty`/`__functionArgs`/`__toString`.

Since these attributes have a low-risk of causing a stack overflow
(because these are flat attr-sets or even functions), ignoring them in
`withRecursion` seems like a valid solution.
Diffstat (limited to 'lib/generators.nix')
-rw-r--r--lib/generators.nix12
1 files changed, 11 insertions, 1 deletions
diff --git a/lib/generators.nix b/lib/generators.nix
index 3bc0fee332aea..431b93c4ebbc6 100644
--- a/lib/generators.nix
+++ b/lib/generators.nix
@@ -251,6 +251,16 @@ rec {
     }:
       assert builtins.isInt depthLimit;
       let
+        specialAttrs = [
+          "__functor"
+          "__functionArgs"
+          "__toString"
+          "__pretty"
+        ];
+        stepIntoAttr = evalNext: name:
+          if builtins.elem name specialAttrs
+            then id
+            else evalNext;
         transform = depth:
           if depthLimit != null && depth > depthLimit then
             if throwOnDepthLimit
@@ -261,7 +271,7 @@ rec {
           let
             evalNext = x: mapAny (depth + 1) (transform (depth + 1) x);
           in
-            if isAttrs v then mapAttrs (const evalNext) v
+            if isAttrs v then mapAttrs (stepIntoAttr evalNext) v
             else if isList v then map evalNext v
             else transform (depth + 1) v;
       in