tvix: incorrect infinite recursion when evaluating cross nixpkgs

#273
Opened by sterni at 2023-05-27T09·36+00

With nixpkgs revision 4f8d9de9a6e03196a739cc42e373d2824fe664e7 and tvix r/6207:

$ tvix -E '(import ~/src/nix/nixpkgs { crossSystem = "aarch64-linux"; }).buildPackages.gcc-unwrapped.drvPath'
note: while evaluating this Nix code
     --> [code]:1:1
      |
1     | (import ~/src/nix/nixpkgs { crossSystem = "aarch64-linux"; }).buildPackages.gcc-unwrapped.drvPath
      | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: while evaluating this as native code (force)
     --> [code]:1:1
      |
1     | (import ~/src/nix/nixpkgs { crossSystem = "aarch64-linux"; }).buildPackages.gcc-unwrapped.drvPath
      | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: while evaluating this Nix code
     --> /home/lukas/src/nix/nixpkgs/pkgs/top-level/all-packages.nix:14895:19
      |
14895 |   gcc-unwrapped = gcc.cc;
      |                   ^^^^^^
note: while evaluating this as native code (force)
     --> /home/lukas/src/nix/nixpkgs/pkgs/top-level/all-packages.nix:14895:19
      |
14895 |   gcc-unwrapped = gcc.cc;
      |                   ^^^
note: while evaluating this Nix code
     --> /home/lukas/src/nix/nixpkgs/lib/meta.nix:16:5
      |
16    |     drv // { meta = (drv.meta or {}) // newAttrs; };
      |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: while evaluating this as native code (force)
     --> /home/lukas/src/nix/nixpkgs/lib/meta.nix:16:5
      |
16    |     drv // { meta = (drv.meta or {}) // newAttrs; };
      |     ^^^
note: while evaluating this Nix code
     --> /home/lukas/src/nix/nixpkgs/lib/customisation.nix:92:7
      |
92    | /       if builtins.isAttrs result then
93    | |         result // {
94    | |           override = overrideArgs;
95    | |           overrideDerivation = fdrv: overrideResult (x: overrideDerivation x fdrv);
...     |
103   | |         }
104   | |       else result;
      | |_________________^
note: while evaluating this as native code (force)
     --> /home/lukas/src/nix/nixpkgs/lib/customisation.nix:92:10
      |
92    |       if builtins.isAttrs result then
      |          ^^^^^^^^^^^^^^^^^^^^^^^
note: while evaluating this Nix code
     --> /home/lukas/src/nix/nixpkgs/pkgs/build-support/cc-wrapper/default.nix:152:1
      |
152   | / assert libc_bin == bintools.libc_bin;
153   | | assert libc_dev == bintools.libc_dev;
154   | | assert libc_lib == bintools.libc_lib;
155   | | assert nativeTools == bintools.nativeTools;
...     |
615   | |   };
616   | | }
      | |_^
note: while evaluating this as native code (force)
     --> /home/lukas/src/nix/nixpkgs/pkgs/build-support/cc-wrapper/default.nix:152:8
      |
152   | assert libc_bin == bintools.libc_bin;
      |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: while evaluating this as native code (getAttr)
     --> <src-builtins/derivation.nix>:26:19
      |
26    |         outPath = builtins.getAttr outputName strict;
      |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: while evaluating this as native code (derivationStrict)
     --> <src-builtins/derivation.nix>:14:12
      |
14    |   strict = derivationStrict drvAttrs;
      |            ^^^^^^^^^^^^^^^^^^^^^^^^^
note: while evaluating this Nix code
     --> /home/lukas/src/nix/nixpkgs/lib/attrsets.nix:893:5
      |
893   | /     if ! pkg ? outputSpecified || ! pkg.outputSpecified
894   | |       then pkg.${output} or pkg.out or pkg
895   | |       else pkg;
      | |______________^
note: while evaluating this as native code (force)
     --> /home/lukas/src/nix/nixpkgs/lib/attrsets.nix:893:8
      |
893   |     if ! pkg ? outputSpecified || ! pkg.outputSpecified
      |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: while evaluating this as native code (force)
     --> /home/lukas/src/nix/nixpkgs/lib/attrsets.nix:893:10
      |
893   |     if ! pkg ? outputSpecified || ! pkg.outputSpecified
      |          ^^^^^^^^^^^^^^^^^^^^^
note: while evaluating this Nix code
     --> /home/lukas/src/nix/nixpkgs/pkgs/stdenv/generic/make-derivation.nix:244:18
      |
244   |       (map (drv: drv.__spliced.buildHost or drv) (checkDependencyList "nativeBuildInputs" nativeBuildInputs'))
      |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: while evaluating this as native code (force)
     --> /home/lukas/src/nix/nixpkgs/pkgs/stdenv/generic/make-derivation.nix:244:22
      |
244   |       (map (drv: drv.__spliced.buildHost or drv) (checkDependencyList "nativeBuildInputs" nativeBuildInputs'))
      |                      ^^^^^^^^^
note: while evaluating this Nix code
     --> /home/lukas/src/nix/nixpkgs/pkgs/stdenv/generic/make-derivation.nix:217:5
      |
217   | /     if lib.isDerivation dep || dep == null || builtins.typeOf dep == "string" || builtins.typeOf dep == "path" then dep
218   | |     else if lib.isList dep then checkDependencyList' ([index] ++ positions) name dep
219   | |     else throw "Dependency is not of a valid type: ${lib.concatMapStrings (ix: "element ${toString ix} of ") ([index] ++ positions)}${name} for ${attrs.name or attrs.pname}");
      | |_____________________________________________________________________________________________________________________________________________________________________________^
note: while evaluating this as native code (force)
     --> /home/lukas/src/nix/nixpkgs/pkgs/stdenv/generic/make-derivation.nix:217:8
      |
217   |     if lib.isDerivation dep || dep == null || builtins.typeOf dep == "string" || builtins.typeOf dep == "path" then dep
      |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: while evaluating this as native code (force)
     --> /home/lukas/src/nix/nixpkgs/pkgs/stdenv/generic/make-derivation.nix:217:8
      |
217   |     if lib.isDerivation dep || dep == null || builtins.typeOf dep == "string" || builtins.typeOf dep == "path" then dep
      |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: while evaluating this as native code (force)
     --> /home/lukas/src/nix/nixpkgs/pkgs/stdenv/generic/make-derivation.nix:217:8
      |
217   |     if lib.isDerivation dep || dep == null || builtins.typeOf dep == "string" || builtins.typeOf dep == "path" then dep
      |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: while evaluating this as native code (force)
     --> /home/lukas/src/nix/nixpkgs/pkgs/stdenv/generic/make-derivation.nix:217:8
      |
217   |     if lib.isDerivation dep || dep == null || builtins.typeOf dep == "string" || builtins.typeOf dep == "path" then dep
      |        ^^^^^^^^^^^^^^^^^^^^
note: while evaluating this Nix code
     --> /home/lukas/src/nix/nixpkgs/lib/attrsets.nix:636:12
      |
636   |     value: value.type or null == "derivation";
      |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: while evaluating this as native code (force)
     --> /home/lukas/src/nix/nixpkgs/lib/attrsets.nix:636:12
      |
636   |     value: value.type or null == "derivation";
      |            ^^^^^^^^^^^^^^^^^^
note: while evaluating this as native code (force)
     --> /home/lukas/src/nix/nixpkgs/lib/attrsets.nix:636:18
      |
636   |     value: value.type or null == "derivation";
      |                  ^^^^
note: while evaluating this Nix code
     --> /home/lukas/src/nix/nixpkgs/pkgs/top-level/splice.nix:76:11
      |
76    | /           if lib.isDerivation defaultValue then augmentedValue // spliceReal {
77    | |             pkgsBuildBuild = tryGetOutputs valueBuildBuild;
78    | |             pkgsBuildHost = tryGetOutputs valueBuildHost;
79    | |             pkgsBuildTarget = tryGetOutputs valueBuildTarget;
...     |
94    | |                 # `__functor__` for functions instead.
95    | |               } else defaultValue;
      | |_________________________________^
note: while evaluating this as native code (force)
     --> /home/lukas/src/nix/nixpkgs/pkgs/top-level/splice.nix:76:14
      |
76    |           if lib.isDerivation defaultValue then augmentedValue // spliceReal {
      |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: while evaluating this Nix code
     --> /home/lukas/src/nix/nixpkgs/lib/attrsets.nix:636:12
      |
636   |     value: value.type or null == "derivation";
      |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: while evaluating this as native code (force)
     --> /home/lukas/src/nix/nixpkgs/lib/attrsets.nix:636:12
      |
636   |     value: value.type or null == "derivation";
      |            ^^^^^^^^^^^^^^^^^^
note: while evaluating this as native code (force)
     --> /home/lukas/src/nix/nixpkgs/lib/attrsets.nix:636:18
      |
636   |     value: value.type or null == "derivation";
      |                  ^^^^
note: while evaluating this Nix code
     --> /home/lukas/src/nix/nixpkgs/pkgs/development/interpreters/python/default.nix:208:20
      |
208   |     python3Minimal = (callPackage ./cpython ({
      |  ____________________^
209   | |     self = __splicedPackages.python3Minimal;
210   | |     inherit passthruFun;
211   | |     pythonAttr = "python3Minimal";
...     |
234   | |     pname = "python3-minimal";
235   | |   });
      | |____^
note: while evaluating this as native code (force)
     --> /home/lukas/src/nix/nixpkgs/pkgs/development/interpreters/python/default.nix:208:20
      |
208   |     python3Minimal = (callPackage ./cpython ({
      |  ____________________^
209   | |     self = __splicedPackages.python3Minimal;
210   | |     inherit passthruFun;
211   | |     pythonAttr = "python3Minimal";
...     |
230   | |     mimetypesSupport = false;
231   | |   } // sources.python310)).overrideAttrs(old: {
      | |________________________________________^
note: while evaluating this as native code (force)
     --> /home/lukas/src/nix/nixpkgs/pkgs/development/interpreters/python/default.nix:208:20
      |
208   |     python3Minimal = (callPackage ./cpython ({
      |  ____________________^
209   | |     self = __splicedPackages.python3Minimal;
210   | |     inherit passthruFun;
211   | |     pythonAttr = "python3Minimal";
...     |
230   | |     mimetypesSupport = false;
231   | |   } // sources.python310)).overrideAttrs(old: {
      | |__________________________^
note: while evaluating this Nix code
     --> /home/lukas/src/nix/nixpkgs/lib/customisation.nix:92:7
      |
92    | /       if builtins.isAttrs result then
93    | |         result // {
94    | |           override = overrideArgs;
95    | |           overrideDerivation = fdrv: overrideResult (x: overrideDerivation x fdrv);
...     |
103   | |         }
104   | |       else result;
      | |_________________^
note: while evaluating this as native code (force)
     --> /home/lukas/src/nix/nixpkgs/lib/customisation.nix:92:10
      |
92    |       if builtins.isAttrs result then
      |          ^^^^^^^^^^^^^^^^^^^^^^^
note: while evaluating this Nix code
     --> /home/lukas/src/nix/nixpkgs/pkgs/development/interpreters/python/cpython/default.nix:83:1
      |
83    | / let
84    | |   # some python packages need legacy ciphers, so we're using openssl 3, but with that config
85    | |   # null check for Minimal
86    | |   openssl' = if openssl != null then openssl_legacy else null;
...     |
553   | |   };
554   | | }
      | |_^
note: while evaluating this as native code (force)
     --> /home/lukas/src/nix/nixpkgs/pkgs/development/interpreters/python/cpython/default.nix:138:26
      |
138   |   hasDistutilsCxxPatch = !(stdenv.cc.isGNU or false);
      |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: while evaluating this Nix code
     --> /home/lukas/src/nix/nixpkgs/pkgs/development/interpreters/python/cpython/default.nix:138:28
      |
138   |   hasDistutilsCxxPatch = !(stdenv.cc.isGNU or false);
      |                            ^^^^^^^^^^^^^^^^^^^^^^^^
note: while evaluating this as native code (force)
     --> /home/lukas/src/nix/nixpkgs/pkgs/development/interpreters/python/cpython/default.nix:138:38
      |
138   |   hasDistutilsCxxPatch = !(stdenv.cc.isGNU or false);
      |                                      ^^^^^
error[E014]: infinite recursion encountered
     --> /home/lukas/src/nix/nixpkgs/pkgs/development/interpreters/python/cpython/default.nix:138:38
      |
138   |      hasDistutilsCxxPatch = !(stdenv.cc.isGNU or false);
      |                                         ^^^^^ but then requested again here during its own evaluation
      | 
     ::: /home/lukas/src/nix/nixpkgs/pkgs/top-level/all-packages.nix
      |
14886 |      inherit (let
      |   ____________-
      |  |____________|
      | ||
14887 | ||       num =
14888 | ||         if (with stdenv.targetPlatform; isVc4 || libc == "relibc") then 6
14889 | ||         else 12;
...     ||
14893 | ||       gccFun = callPackage (../development/compilers/gcc + "/${numS}");
14894 | ||     }) gcc gccFun;
      | ||     -
      | ||_____|
      | |______this lazily-evaluated code
      |        which was instantiated here
14895 |      gcc-unwrapped = gcc.cc;
      |                      --- was first requested to be evaluated here

C++ Nix evaluates this successfully. The location that triggers this (stdenv.cc.isGNU or false) points toward an overeager evaluation problem—even in nixpkgs with C++ Nix it is kind of easy to run into this recursion when working on cross (typically when looking up information about the next stage (targetPackages) which looks up information about the previous stage (its buildPackages)), see e.g. 766f5ffb761bc916ea0f270f472d04ab30664d52.

  1. Interestingly, applying the following patch to nixpkgs solves the problem:

    diff --git a/pkgs/development/interpreters/python/cpython/default.nix b/pkgs/development/interpreters/python/cpython/default.nix
    index a089dbf2002..2bc0e6729cd 100644
    --- a/pkgs/development/interpreters/python/cpython/default.nix
    +++ b/pkgs/development/interpreters/python/cpython/default.nix
    @@ -135,7 +135,7 @@ let
         ++ optionals enableFramework [ Cocoa ]
         ++ optionals tzdataSupport [ tzdata ];  # `zoneinfo` module
     
    -  hasDistutilsCxxPatch = !(stdenv.cc.isGNU or false);
    +  hasDistutilsCxxPatch = builtins.seq (stdenv.cc ? isGNU) (!(stdenv.cc.isGNU or false));
     
       pythonForBuildInterpreter = if stdenv.hostPlatform == stdenv.buildPlatform then
         "$out/bin/python"
    

    This also does not necessarily have anything to do with the builtin, since we can achieve a similar effect only with an expression that is compiled entirely to bytecode:

      hasDistutilsCxxPatch = assert (stdenv ? cc.isGNU) || true; !(stdenv.cc.isGNU or false);
    

    sterni at 2023-05-27T09·49+00

  2. The diagnosis is that the expression is not thunked enough, since forcing it to be wrapped in a thunk using function application resolves the issue:

    diff --git a/pkgs/development/interpreters/python/cpython/default.nix b/pkgs/development/interpreters/python/cpython/default.nix
    index a089dbf2002..794e64dbd29 100644
    --- a/pkgs/development/interpreters/python/cpython/default.nix
    +++ b/pkgs/development/interpreters/python/cpython/default.nix
    @@ -135,7 +135,7 @@ let
         ++ optionals enableFramework [ Cocoa ]
         ++ optionals tzdataSupport [ tzdata ];  # `zoneinfo` module
     
    -  hasDistutilsCxxPatch = !(stdenv.cc.isGNU or false);
    +  hasDistutilsCxxPatch = (x: x) (!(stdenv.cc.isGNU or false));
     
       pythonForBuildInterpreter = if stdenv.hostPlatform == stdenv.buildPlatform then
         "$out/bin/python"
    

    The question is: What is the correct fix? Do we…

    • …wrap all select_or expressions in an extra thunk? Does this need to happen for other expression types as well?
    • …wrap all attribute value expressions in an extra thunk? Same for lists?

    Maybe a look in the C++ Nix source is prudent again…

    sterni at 2023-05-27T10·38+00

  3. cl/8654 gets us gcc, but not the cross stdenv due to something similar to b/261.

    sterni at 2023-05-27T12·26+00

  4. sterni closed this issue at 2023-05-30T21·53+00