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

Module system types.attrTag shadows name argument for inner submodules #351888

Open
8aed opened this issue Oct 28, 2024 · 3 comments
Open

Module system types.attrTag shadows name argument for inner submodules #351888

8aed opened this issue Oct 28, 2024 · 3 comments
Labels
0.kind: bug Something is broken 6.topic: module system About "NixOS" module system internals

Comments

@8aed
Copy link
Contributor

8aed commented Oct 28, 2024

Describe the bug

If I have an option with type attrsOf (attrTag { ... }), and use a submodule for the types of the tag, this submodule receives the tag name in name, rather than the parent attribute's name.

I understand this may be expected given how submodulesWith is defined (it has no way to know that last loc is a tag name), however, unless I've missed something obvious, it means that submodules can not get access to the parent attribute's name when used in a tag type.

I've tried to fix this myself but it kept breaking other things, and I haven't yet found a way to overcome this limitation/bug.

Steps To Reproduce

# test.nix
let pkgs = import ./nix {}; lib = pkgs.lib;
in with lib; with lib.types; let
  result = evalModules {
    modules = [
      ({ lib, ... }: let 
        mySubmodule = lib.types.submodule (
          { name, ... }: {
            options.myName = lib.mkOption {
              default = name;
              type = lib.types.str;
            };
          }
        );
      in {
        options.tagged = lib.mkOption {
          default = { foo.myTag = {}; bar.myTag = {}; };
          type = attrsOf (lib.types.attrTag {
            myTag = lib.mkOption {
              description = " a tag";
              type = mySubmodule;
            };
          });
        };

        options.notTagged = lib.mkOption {
          type = attrsOf (mySubmodule);
          default = { foo = {}; bar = {}; };
        };
      })
    ];
  };
in result.config
[nix-shell:/tmp/ghjk5]$ nix-instantiate --eval test.nix --strict --json
{"notTagged": {"bar":{"myName":"bar"},"foo":{"myName":"foo"}},
  "tagged":{"bar":{"myTag":{"myName":"myTag"}},"foo":{"myTag":{"myName":"myTag"}}}}

Expected behavior

[nix-shell:/tmp/ghjk5]$ nix-instantiate --eval test.nix --strict --json
{"notTagged": {"bar":{"myName":"bar"},"foo":{"myName":"foo"}},
  "tagged":{"bar":{"myTag":{"myName":"bar"}},"foo":{"myTag":{"myName":"foo"}}}}

Notify maintainers

@roberth
@infinisil

Metadata

Tested on nixos-unstable with Nix 2.18

@8aed 8aed added the 0.kind: bug Something is broken label Oct 28, 2024
@8aed
Copy link
Contributor Author

8aed commented Oct 29, 2024

One thing I tried that can fix this is making submoduleWith receive an index to read loc (in merge), rather than just taking last loc, and specializing submodule in taggedSubmodule to make it skip the tag name. There is also the possibility of specializing attrTag (attrTagWithSubmodules).

If one of these solutions is acceptable, I'd be happy to send a PR, but wanted to discuss it here first.

@infinisil infinisil added the 6.topic: module system About "NixOS" module system internals label Nov 1, 2024
@infinisil
Copy link
Member

Thanks for the report! I think the best way to resolve that would go in the direction of #97378 (comment), see also #344216

@roberth
Copy link
Member

roberth commented Nov 1, 2024

last loc was always a bit of a hack imo.

#344216

Specifically this, I would think:

itemTypeFunction = name: submoduleWith { modules = f name; specialArgs = g name; };

So you'd be able to write

type = attrsWith {
  itemTypeFunction = name:
    attrTag {
      foo = mkOption {
        type = submoduleWith {
          # This seems like a good idea, but not strictly necessary as long as all usages or in `modules` below...
          specialArgs.name = name;
          modules = [
            { # ... because you could just use name directly here
              options.myName = mkOption { default = name; };
            }
          ];
        };
      };
    };
};

I think this is a good strategy. It just depends on having an attrsWith function to extend. Unfortunately the first commit of #344216 does too much. Otherwise, I'd merge that commit right away.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
0.kind: bug Something is broken 6.topic: module system About "NixOS" module system internals
Projects
None yet
Development

No branches or pull requests

3 participants