diff options
author | Maximilian Bosch <maximilian@mbosch.me> | 2022-04-12 12:26:25 +0200 |
---|---|---|
committer | Maximilian Bosch <maximilian@mbosch.me> | 2022-04-12 12:34:23 +0200 |
commit | 72037880688ff9c6f4188a5746c3ab5ce7215628 (patch) | |
tree | 6d4f84448840b119ca880ef6b367f8adcd9e5b1a /lib/generators.nix | |
parent | dbe672e4a718e400efa4ae71a9c91cc0739feb76 (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.nix | 12 |
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 |