Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Miscellaneous improvements for positioning in eval-errors #4440

Merged
merged 5 commits into from Apr 23, 2021

Conversation

Ma27
Copy link
Member

@Ma27 Ma27 commented Jan 8, 2021

This is still a work-in-progress PR to improve the position of errors showed by Nix. I'm happy to do more work on this to get this actually mergable, but first I'd like to gather some feedback (cc @domenkozar @edolstra @bburdette @Ericson2314 ).

To summarize, the following changes are on this branch:

  • primops/derivation: use position of currently evaluated attribute

    • The position of the name-attribute appears in the trace.
    • If e.g. meta has no outPath-attribute, a cannot coerce set to string error will be thrown where pos points to name = which is
      highly misleading.
  • libexpr: misc improvements for proper error position

    When working on some more complex Nix code, there are sometimes rather
    unhelpful or misleading error messages, especially if coerce-errors are
    thrown.

    This patch is a first steps towards improving that. I'm happy to file
    more changes after that, but I'd like to gather some feedback first.

    To summarize, this patch does the following things:

    • Attrsets (a.k.a. Bindings in libexpr) now have a Pos. This is
      helpful e.g. to identify which attribute-set in listToAttrs is
      invalid.

    • The Value-struct has a new method named determinePos which tries
      to guess the position of a value and falls back to a default if that's
      not possible.

      This can be used to provide better messages if a coercion fails.

    • The new determinePos-API is used by builtins.concatMap now. With
      that change, Nix shows the exact position in the error where a wrong
      value was returned by the lambda.

      To make sure it's still obvious that concatMap is the problem,
      another stack-frame was added.

    • The changes described above can be added to every other primop, but
      first I'd like to get some feedback about the overall approach.

@domenkozar
Copy link
Member

It would be great to see before/after example!

@Ma27
Copy link
Member Author

Ma27 commented Jan 9, 2021

You're absolutely right, sorry for that!

  • Let's say you have a derivation (i.e. builtins.derivation is used) where a meta-section is defined, but without an outPath-attribute:

    builtins.derivation {
      name = "foo";
      builder = "/bin/sh";
      system = builtins.currentSystem;
      meta = {
        maintainers = [ ];
      };
      #meta.outPath = placeholder "out";
    }

    On current nixUnstable, the output of nix-build test2.nix --show-trace looks like this:

    error: --- TypeError --------------------------------------------------------------------------- nix-build
    at: (2:3) in file: /home/ma27/Projects/nix/test2.nix
    
         1| builtins.derivation {
         2|   name = "foo";
          |   ^
         3|   builder = "/bin/sh";
    
    cannot coerce a set to a string
    ----------------------------------------------- show-trace -----------------------------------------------
    trace: while evaluating the attribute 'meta' of the derivation 'foo'
    at: (2:3) in file: /home/ma27/Projects/nix/test2.nix
    
         1| builtins.derivation {
         2|   name = "foo";
          |   ^
         3|   builder = "/bin/sh";
    

    Which is a bit weird since the name-attribute is a string. On my branch it looks like this:

    error: --- TypeError --------------------------------------------------------------------------- nix-build
    at: (5:3) in file: /home/ma27/Projects/nix/test2.nix
    
         4|   system = builtins.currentSystem;
         5|   meta = {
          |   ^
         6|     maintainers = [ ];
    
    cannot coerce a set to a string
    ----------------------------------------------- show-trace -----------------------------------------------
    trace: while evaluating the attribute 'meta' of the derivation 'foo'
    at: (2:3) in file: /home/ma27/Projects/nix/test2.nix
    
         1| builtins.derivation {
         2|   name = "foo";
          |   ^
         3|   builder = "/bin/sh";
    
    
  • Another fix that's already on this branch is for builtins.listToAttrs. An expression that IMHO demonstrates its usefulness looks like this:

    let
      bar = x: if x == 4 then { name = "foo"; vallue = "bar"; } else {
        name = "foo${toString x}";
        value = 23;
      };
      foo = [ 1 2 3 4 ];
    in builtins.listToAttrs (
      map bar foo
    )

    On nixUnstable, the error (with --show-trace) looks like this:

    error: --- TypeError --------------------------------------------------------------------- nix-instantiate
    at: (7:4) in file: /home/ma27/Projects/nix/test5.nix
    
         6|   foo = [ 1 2 3 4 ];
         7| in builtins.listToAttrs (
          |    ^
         8|   map bar foo
    
    'value' attribute missing in a call to 'listToAttrs'
    

    On my branch it looks like this:

      error: --- TypeError --------------------------------------------------------------------- nix-instantiate
    at: (2:27) in file: /home/ma27/Projects/nix/test5.nix
    
         1| let
         2|   bar = x: if x == 4 then { name = "foo"; vallue = "bar"; } else {
          |                           ^
         3|     name = "foo${toString x}";
    
    'value' attribute missing for 'listToAttrs'
    ----------------------------------------------- show-trace -----------------------------------------------
    trace: while invoking 'listToAttrs'
    at: (7:4) in file: /home/ma27/Projects/nix/test5.nix
    
         6|   foo = [ 1 2 3 4 ];
         7| in builtins.listToAttrs (
          |    ^
         8|   map bar foo
    
    
  • determinePos is a new function on the Value-struct which can guess the position of a faulting expression and otherwise returns a fallback. It's only used in this PoC by builtins.concatMap, as soon as I have more feedback, I'll implement it on other builtins as well.

    First of all, if the lambda in concatMap returns an invalid value, the error generally looks like this on nixUnstable:

      error: --- TypeError --------------------------------------------------------------------- nix-instantiate
    at: (7:3) in file: /home/ma27/Projects/nix/test3.nix
    
         6| in
         7|   concatMap foo [ 1 2 3 4 ]
          |   ^
    
    value is a function while a list was expected
    

    If foo returns a function, the error looks like this on my branch:

      error: --- TypeError --------------------------------------------------------------------- nix-instantiate
    at: (5:12) in file: /home/ma27/Projects/nix/test3.nix
    
         4| let
         5|   foo = a: b: a;
          |            ^
         6| in
    
    value is a function while a list was expected
    ----------------------------------------------- show-trace -----------------------------------------------
    trace: while invoking 'concatMap'
    at: (7:3) in file: /home/ma27/Projects/nix/test3.nix
    
         6| in
         7|   concatMap foo [ 1 2 3 4 ]
          |   ^
    
    

    If foo returns an attr-set, it would look like this on my branch:

    error: --- TypeError --------------------------------------------------------------------- nix-instantiate
    at: (5:12) in file: /home/ma27/Projects/nix/test3.nix
    
         4| let
         5|   foo = a: {};
          |            ^
         6| in
    
    value is a set while a list was expected
    ----------------------------------------------- show-trace -----------------------------------------------
    trace: while invoking 'concatMap'
    at: (7:3) in file: /home/ma27/Projects/nix/test3.nix
    
         6| in
         7|   concatMap foo [ 1 2 3 4 ]
          |   ^
    

    Also, partial appliances are supported:

    error: --- TypeError --------------------------------------------------------------------- nix-instantiate
    at: (5:10) in file: /home/ma27/Projects/nix/test3.nix
    
         4| let
         5|   n = x: y: x;
          |          ^
         6|   foo = a: n a;
    
    value is a function while a list was expected
    ----------------------------------------------- show-trace -----------------------------------------------
    trace: while invoking 'concatMap'
    at: (8:3) in file: /home/ma27/Projects/nix/test3.nix
    
         7| in
         8|   concatMap foo [ 1 2 3 4 ]
          |   ^
    

    If the value returned by foo in this expression doesn't have a Pos (e.g. if it's a NixInt), the position of foo is provided:

    error: --- TypeError --------------------------------------------------------------------- nix-instantiate
    at: (5:9) in file: /home/ma27/Projects/nix/test3.nix
    
         4| let
         5|   foo = a: 1;
          |         ^
         6| in
    
    value is an integer while a list was expected
    ----------------------------------------------- show-trace -----------------------------------------------
    trace: while invoking 'concatMap'
    at: (7:3) in file: /home/ma27/Projects/nix/test3.nix
    
         6| in
         7|   concatMap foo [ 1 2 3 4 ]
          |   ^
    
    

@nixos-discourse
Copy link

This pull request has been mentioned on NixOS Discourse. There might be relevant details there:

https://discourse.nixos.org/t/prs-ready-for-review/3032/439

@edolstra
Copy link
Member

Looks good to me. The only concern is adding a Pos field to Bindings since that may be a non-trivial increase in memory consumption...

@Ma27
Copy link
Member Author

Ma27 commented Jan 21, 2021

@edolstra yeah, I'm afraid so. As I'm not as experienced with the codebase as you are, may I ask if you have an alternative idea with a smaller memory footprint? Otherwise I'd probably do some benchmarking with a few more complex expressions to see the actual difference.

As soon as we've resolved that (and I have sufficient time ^^), I'd continue on that PR here :)

@Ma27
Copy link
Member Author

Ma27 commented Jan 26, 2021

@edolstra so I did a few brief tests now by comparing Nix master (f15f0b8) vs my branch (locally rebased against f15f0b8).

For comparison, I decided to use a few NixOS tests:

  • nixos/tests/nextcloud/basic.nix:
    Nix from master:
  → /run/current-system/sw/bin/time -f "%P %M" nix-instantiate ../nixpkgs/nixos/tests/nextcloud/basic.nix
warning: you did not specify '--add-root'; the result might be removed by the garbage collector
/nix/store/nv0hsarlxy63kav65fjz8qrvxgjc1v4w-vm-test-run-nextcloud-basic.drv
101% 633464

Nix from my branch:

  → /run/current-system/sw/bin/time -f "%P %M" nix-instantiate ../nixpkgs/nixos/tests/nextcloud/basic.nix
  warning: you did not specify '--add-root'; the result might be removed by the garbage collector
/nix/store/nv0hsarlxy63kav65fjz8qrvxgjc1v4w-vm-test-run-nextcloud-basic.drv
105% 647576

As you can see the difference is ~20MB peak memory consumption.

  • nixos/tests/networking.nix (with --arg networkd true):
    Nix from master:
  → /run/current-system/sw/bin/time -f "%P %M" nix-instantiate ../nixpkgs/nixos/tests/networking.nix --arg networkd true
warning: you did not specify '--add-root'; the result might be removed by the garbage collector
/nix/store/smxim11pyx4scslbnwv1bp5z1qg1whbg-vm-test-run-Bond-Networking-Networkd.drv
/nix/store/zh8970ylxnxlhrc2a2px639b1g873va1-vm-test-run-Bridge-Networking-Networkd.drv
/nix/store/gni8llcphm4n20zc4y1qq8mpjfbbhxjv-vm-test-run-OneInterfaceDHCP-Networking-Networkd.drv
/nix/store/kz300gbfm5wakwpyf4nc38wvnv9nhi26-vm-test-run-SimpleDHCP-Networking-Networkd.drv
/nix/store/kdpzgxd9k74prh8wk4j0767gfzagjc9h-vm-test-run-Link-Networking-Networkd.drv
/nix/store/nvm4w3hzyraa6xfjlywpwg82m1ysxh3k-vm-test-run-Loopback-Networking-Networkd.drv
/nix/store/h6mrdq5qx1nbqrswfkfhl9fav6xl8azl-vm-test-run-MACVLAN-Networking-Networkd.drv
/nix/store/8bh9j7n9dpx10pqh8v24zjxxz891h08n-vm-test-run-Privacy-Networking-Networkd.drv
/nix/store/6v7pvqpc1p3w68796maw8w5f3zmcmqa9-vm-test-run-routes-Networking-Networkd.drv
/nix/store/8qf08qi2h84nj3lmlppicjc32r2davwr-vm-test-run-Sit-Networking-Networkd.drv
/nix/store/riq0fnf20x1kmm19nha5fp8ckca0xv74-vm-test-run-Static-Networking-Networkd.drv
/nix/store/97q63q462nx3zpkv765jb0lnl9ks8mmg-vm-test-run-Virtual-Networking-Networkd.drv
/nix/store/a570d5j046c7zcc87hsja4wnwzs1a5qm-vm-test-run-vlan-Networking-Networkd.drv
117% 5714416

Nix from my branch:

→ /run/current-system/sw/bin/time -f "%P %M" nix-instantiate ../nixpkgs/nixos/tests/networking.nix --arg networkd true
/nix/store/smxim11pyx4scslbnwv1bp5z1qg1whbg-vm-test-run-Bond-Networking-Networkd.drv
/nix/store/zh8970ylxnxlhrc2a2px639b1g873va1-vm-test-run-Bridge-Networking-Networkd.drv
/nix/store/gni8llcphm4n20zc4y1qq8mpjfbbhxjv-vm-test-run-OneInterfaceDHCP-Networking-Networkd.drv
/nix/store/kz300gbfm5wakwpyf4nc38wvnv9nhi26-vm-test-run-SimpleDHCP-Networking-Networkd.drv
/nix/store/kdpzgxd9k74prh8wk4j0767gfzagjc9h-vm-test-run-Link-Networking-Networkd.drv
/nix/store/nvm4w3hzyraa6xfjlywpwg82m1ysxh3k-vm-test-run-Loopback-Networking-Networkd.drv
/nix/store/h6mrdq5qx1nbqrswfkfhl9fav6xl8azl-vm-test-run-MACVLAN-Networking-Networkd.drv
/nix/store/8bh9j7n9dpx10pqh8v24zjxxz891h08n-vm-test-run-Privacy-Networking-Networkd.drv
/nix/store/6v7pvqpc1p3w68796maw8w5f3zmcmqa9-vm-test-run-routes-Networking-Networkd.drv
/nix/store/8qf08qi2h84nj3lmlppicjc32r2davwr-vm-test-run-Sit-Networking-Networkd.drv
/nix/store/riq0fnf20x1kmm19nha5fp8ckca0xv74-vm-test-run-Static-Networking-Networkd.drv
/nix/store/97q63q462nx3zpkv765jb0lnl9ks8mmg-vm-test-run-Virtual-Networking-Networkd.drv
/nix/store/a570d5j046c7zcc87hsja4wnwzs1a5qm-vm-test-run-vlan-Networking-Networkd.drv
112% 5660756  

It's kinda interesting that my branch actually consumes less memory than Nix master (I tried this three times in a row with an equal result). @edolstra may have an idea why this is happening.

I assume that the increase is a bit too much to use this by default (feel free to disagree though). I'm wondering now how we should proceed here: do you have an idea if we can reduce the memory consumption there somehow? Or by making this behavior optional?

@Ma27
Copy link
Member Author

Ma27 commented Mar 8, 2021

cc @edolstra given my latest comment, how shall we proceed here? :)

@Ma27
Copy link
Member Author

Ma27 commented Mar 27, 2021

Rebased onto latest master.

cc @edolstra given my latest comments, how shall we proceed here? :)

@edolstra
Copy link
Member

edolstra commented Apr 9, 2021

Peak memory consumption is a bit tricky to measure since it depends on whether/when GC kicks in. A more reproducible metric is to run with NIX_SHOW_STATS=1 and look at the sets field:

$ NIX_SHOW_STATS=1 nix build nixpkgs#hello
...
  "sets": {
    "number": 14196,
    "bytes": 27683376,
    "elements": 1148742
  },

@Ma27
Copy link
Member Author

Ma27 commented Apr 9, 2021

@edolstra @domenkozar so I just evaluated the networking tests in nixpkgs as they're known to consume a lot of memory.

First of all, let me share the raw results here:
  • nix-instantiate from this branch:
    warning: you did not specify '--add-root'; the result might be removed by the garbage collector
    /nix/store/cjxdyjsif0bw4fz3n90x962cxar7q7s7-vm-test-run-Bond-Networking-Networkd.drv
    /nix/store/5lksf48cxy4186ik0lrqnh398g24706m-vm-test-run-Bridge-Networking-Networkd.drv
    /nix/store/hlm7sxb4zlsa6idg3jmg73x9gzxyg5l5-vm-test-run-OneInterfaceDHCP-Networking-Networkd.drv
    /nix/store/vzvfcp12wfrj2cqy7n11fd9d7lz0ij4q-vm-test-run-SimpleDHCP-Networking-Networkd.drv
    /nix/store/jy4vawggggg1vypd3syxdar7y25h2g1s-vm-test-run-Link-Networking-Networkd.drv
    /nix/store/ma7z083hpvb9vk329lcqadday0yz70s3-vm-test-run-Loopback-Networking-Networkd.drv
    /nix/store/1hdl2w7k9wlmvm45sg4bf71hpvbi7gfq-vm-test-run-MACVLAN-Networking-Networkd.drv
    /nix/store/82f7wfxjf8c368hvbg7nwwl20csf0vc0-vm-test-run-Privacy-Networking-Networkd.drv
    /nix/store/ydx1v08x9mb62410cv8kgirkma0ha7r2-vm-test-run-RenameInterface-Networking-Networkd.drv
    /nix/store/nmywpzvdviiiaxismmc5f4mr4bfyhjn6-vm-test-run-routes-Networking-Networkd.drv
    /nix/store/jg8jz4dkrj7lf5kir91gdwjahqkmsbpb-vm-test-run-Sit-Networking-Networkd.drv
    /nix/store/xnpl45bxvsxc63fcjgjck07d2yqh2sh8-vm-test-run-Static-Networking-Networkd.drv
    /nix/store/88h76mzzqnp2mx710bpanvc9vi1158q4-vm-test-run-Virtual-Networking-Networkd.drv
    /nix/store/my0qhv947ph8dz4s1nn0hvi32bzf5ikh-vm-test-run-vlan-Networking-Networkd.drv
    {
      "cpuTime": 59.631,
      "envs": {
        "number": 50506384,
        "elements": 70724847,
        "bytes": 1373900920
      },
      "list": {
        "elements": 77998120,
        "bytes": 623984960,
        "concats": 2350660
      },
      "values": {
        "number": 101889452,
        "bytes": 2445346848
      },
      "symbols": {
        "number": 83437,
        "bytes": 3008151
      },
      "sets": {
        "number": 14640912,
        "bytes": 4051798680,
        "elements": 159064337
      },
      "sizes": {
        "Env": 16,
        "Value": 24,
        "Bindings": 16,
        "Attr": 24
      },
      "nrOpUpdates": 4278344,
      "nrOpUpdateValuesCopied": 112851782,
      "nrThunks": 67261448,
      "nrAvoided": 51225408,
      "nrLookups": 35325117,
      "nrPrimOpCalls": 18532448,
      "nrFunctionCalls": 45239584,
      "gc": {
        "heapSize": 5438103552,
        "totalBytes": 10060301440
      }
    }
    
  • nix-instantiate from nix (Nix) 2.4pre20210326_dd77f71:
      warning: you did not specify '--add-root'; the result might be removed by the garbage collector
    /nix/store/cjxdyjsif0bw4fz3n90x962cxar7q7s7-vm-test-run-Bond-Networking-Networkd.drv
    /nix/store/5lksf48cxy4186ik0lrqnh398g24706m-vm-test-run-Bridge-Networking-Networkd.drv
    /nix/store/hlm7sxb4zlsa6idg3jmg73x9gzxyg5l5-vm-test-run-OneInterfaceDHCP-Networking-Networkd.drv
    /nix/store/vzvfcp12wfrj2cqy7n11fd9d7lz0ij4q-vm-test-run-SimpleDHCP-Networking-Networkd.drv
    /nix/store/jy4vawggggg1vypd3syxdar7y25h2g1s-vm-test-run-Link-Networking-Networkd.drv
    /nix/store/ma7z083hpvb9vk329lcqadday0yz70s3-vm-test-run-Loopback-Networking-Networkd.drv
    /nix/store/1hdl2w7k9wlmvm45sg4bf71hpvbi7gfq-vm-test-run-MACVLAN-Networking-Networkd.drv
    /nix/store/82f7wfxjf8c368hvbg7nwwl20csf0vc0-vm-test-run-Privacy-Networking-Networkd.drv
    /nix/store/ydx1v08x9mb62410cv8kgirkma0ha7r2-vm-test-run-RenameInterface-Networking-Networkd.drv
    /nix/store/nmywpzvdviiiaxismmc5f4mr4bfyhjn6-vm-test-run-routes-Networking-Networkd.drv
    /nix/store/jg8jz4dkrj7lf5kir91gdwjahqkmsbpb-vm-test-run-Sit-Networking-Networkd.drv
    /nix/store/xnpl45bxvsxc63fcjgjck07d2yqh2sh8-vm-test-run-Static-Networking-Networkd.drv
    /nix/store/88h76mzzqnp2mx710bpanvc9vi1158q4-vm-test-run-Virtual-Networking-Networkd.drv
    /nix/store/my0qhv947ph8dz4s1nn0hvi32bzf5ikh-vm-test-run-vlan-Networking-Networkd.drv
    {
      "cpuTime": 59.1465,
      "envs": {
        "number": 50506384,
        "elements": 70724847,
        "bytes": 1373900920
      },
      "list": {
        "elements": 77998120,
        "bytes": 623984960,
        "concats": 2350660
      },
      "values": {
        "number": 101889453,
        "bytes": 2445346872
      },
      "symbols": {
        "number": 83439,
        "bytes": 3008169
      },
      "sets": {
        "number": 14640912,
        "bytes": 3934671384,
        "elements": 159064337
      },
      "sizes": {
        "Env": 16,
        "Value": 24,
        "Bindings": 8,
        "Attr": 24
      },
      "nrOpUpdates": 4278344,
      "nrOpUpdateValuesCopied": 112851782,
      "nrThunks": 67261448,
      "nrAvoided": 51225408,
      "nrLookups": 35325117,
      "nrPrimOpCalls": 18532448,
      "nrFunctionCalls": 45239584,
      "gc": {
        "heapSize": 5387771904,
        "totalBytes": 9952454032
      }
    }
    

So we basically have 9952454032 vs 10060301440 total bytes here which is an increase from ~1.1% of memory. Given your comment from #nixos-dev "I think a few % increase is not a problem" I think it's fine to get this PR ready soon and merge it into master? :)

src/libexpr/primops.cc Outdated Show resolved Hide resolved
src/libexpr/primops.cc Outdated Show resolved Hide resolved
src/libexpr/primops.cc Outdated Show resolved Hide resolved
* The position of the `name`-attribute appears in the trace.
* If e.g. `meta` has no `outPath`-attribute, a `cannot coerce set to
  string` error will be thrown where `pos` points to `name =` which is
  highly misleading.
When working on some more complex Nix code, there are sometimes rather
unhelpful or misleading error messages, especially if coerce-errors are
thrown.

This patch is a first steps towards improving that. I'm happy to file
more changes after that, but I'd like to gather some feedback first.

To summarize, this patch does the following things:

* Attrsets (a.k.a. `Bindings` in `libexpr`) now have a `Pos`. This is
  helpful e.g. to identify which attribute-set in `listToAttrs` is
  invalid.

* The `Value`-struct has a new method named `determinePos` which tries
  to guess the position of a value and falls back to a default if that's
  not possible.

  This can be used to provide better messages if a coercion fails.

* The new `determinePos`-API is used by `builtins.concatMap` now. With
  that change, Nix shows the exact position in the error where a wrong
  value was returned by the lambda.

  To make sure it's still obvious that `concatMap` is the problem,
  another stack-frame was added.

* The changes described above can be added to every other `primop`, but
  first I'd like to get some feedback about the overall approach.
This now takes care of providing positioning for both the faulting value
and the faulting function call in case of an error.
@Ma27 Ma27 changed the title [WIP] Miscellaneous improvements for positioning in eval-errors Miscellaneous improvements for positioning in eval-errors Apr 13, 2021
@Ma27
Copy link
Member Author

Ma27 commented Apr 13, 2021

@edolstra thanks for your review! I applied your suggestions and rebased onto latest master, so this should be good to go.

A few ideas for this would be to

  • use the determinePos thing for state.force* on a few more places
  • and probably provide positions for even more values then (if it doesn't take up too much RAM).

What do you think of this? I'm happy to work on this somewhere in the future (can't promise that I'll get to it though) if you'd consider this a reasonable thing to do :)

@Ma27
Copy link
Member Author

Ma27 commented Apr 18, 2021

@edolstra do you think we can merge this? Also, what do you think of the idea for further changes regarding error messages? :)

@edolstra edolstra merged commit 293220b into NixOS:master Apr 23, 2021
@Ma27 Ma27 deleted the misc-pos-fixes branch April 23, 2021 09:11
@edolstra
Copy link
Member

@Ma27 Thanks! Yeah, more position info is generally a good thing if it doesn't take too much memory.

@Ma27
Copy link
Member Author

Ma27 commented Apr 23, 2021

Yup :) Will do some further experiments when I have the time and check the new memory footprint with NIX_SHOW_STATS :)

Ma27 referenced this pull request in NixOS/nixpkgs Jul 18, 2021
When inlining a module with a problematic declaration, you usually get
get a not-so helpful error like this:

    $ cat flake.nix
    {
      description = "A very basic flake";
      inputs.nixpkgs.url = path:../.;
      outputs = { self, nixpkgs }: {
        nixosConfigurations.foo = nixpkgs.lib.nixosSystem {
          system = "x86_64-linux";
          modules = [
            ({ lib, ... }: { services.wrong = 2; })
            { services.nginx.enable = true; }
          ];
        };
      };
    }
    $ nixos-rebuild build --flake .#foo -L
    error: The option `services.wrong' does not exist. Definition values:
           - In `<unknown-file>': 2

While it's certainly possible to guess where this comes from, this is
IMHO fairly confusing for beginners (and kinda reminds me of the
infamous "infinite recursion at undefined position"-error).

The module-system determines the position of a declaration using the
`_file`-key: this is either `toString path` if `path` is e.g. a value
from `imports = [ ./foo.nix ]` or the file used as `NIXOS_CONFIG` in
`<nixpkgs/nixos>`.

However such a mechanism doesn't exist (yet) for inlined flake modules,
so I tried to implement this in a fairly basic way:

* For non-path declarations, the position of `modules` inside the
  `flake.nix` which declares these modules is determined by doing
  `unsafeGetAttrPos` on the `modules`-argument of `lib.nixosSystem`.

  So the `flake.nix` from above would now raise the following
  error-message:

        $ nixos-rebuild build --flake .#foo -L
        error: The option `services.wrong' does not exist. Definition values:
               - In `/nix/store/4vi3nhqjyma73ygs4f93q38qjkhkaxw8-source/flake.nix': 2

Co-authored-by: Cole Helbling <cole.e.helbling@outlook.com>
Co-authored-by: Silvan Mosberger <github@infinisil.com>
Co-authored-by: Robert Hensing <robert@roberthensing.nl>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants