diff --git a/lib/tests/modules.sh b/lib/tests/modules.sh index e623c0fb55b86..0a18d092a9657 100755 --- a/lib/tests/modules.sh +++ b/lib/tests/modules.sh @@ -513,6 +513,10 @@ checkConfigOutput '^{}$' config.value.mkmerge ./types-anything/mk-mods.nix checkConfigOutput '^true$' config.value.mkbefore ./types-anything/mk-mods.nix checkConfigOutput '^1$' config.value.nested.foo ./types-anything/mk-mods.nix checkConfigOutput '^"baz"$' config.value.nested.bar.baz ./types-anything/mk-mods.nix +# Function reflection works +checkConfigOutput '^{"p":1,"q":2}$' 'config.applied.merging-args.value' ./types-anything/functions.nix +checkConfigOutput '^{"a":false,"b":false}$' 'config.applied.merging-args.args' ./types-anything/functions.nix + ## types.functionTo checkConfigOutput '^"input is input"$' config.result ./functionTo/trivial.nix diff --git a/lib/tests/modules/types-anything/functions.nix b/lib/tests/modules/types-anything/functions.nix index c29e3a1c5d8c1..7561a38301e04 100644 --- a/lib/tests/modules/types-anything/functions.nix +++ b/lib/tests/modules/types-anything/functions.nix @@ -10,7 +10,19 @@ }; options.applied = lib.mkOption { - default = lib.mapAttrs (name: fun: fun null) config.value; + default = lib.mapAttrs (name: fun: + if name == "merging-args" + then + { + args = + let x = lib.functionArgs fun; + in builtins.deepSeq x x; + value = + let x = fun { a = 1; b = 2; }; + # can't pass --strict easily + in builtins.deepSeq x x; + } + else fun null) config.value; }; config = lib.mkMerge [ @@ -18,10 +30,12 @@ value.single-lambda = x: x; value.multiple-lambdas = x: { inherit x; }; value.merging-lambdas = x: { inherit x; }; + value.merging-args = args@{ a, ... }: { p = a; }; } { value.multiple-lambdas = x: [ x ]; value.merging-lambdas = y: { inherit y; }; + value.merging-args = args@{ b, ... }: { q = b; }; } ]; diff --git a/lib/types.nix b/lib/types.nix index ee1410127b1ae..9cec764f4ba07 100644 --- a/lib/types.nix +++ b/lib/types.nix @@ -15,6 +15,8 @@ let isList isString isStorePath + functionArgs + setFunctionArgs throwIf toDerivation toList @@ -303,12 +305,26 @@ rec { set = (attrsOf anything).merge; # This is the type of packages, only accept a single definition stringCoercibleSet = mergeOneOption; - lambda = loc: defs: arg: anything.merge - (loc ++ [ "" ]) - (map (def: { - file = def.file; - value = def.value arg; - }) defs); + lambda = loc: defs: + let + args = zipAttrsWith + (name: + # All params must have a default for the combination + # to have one. AND on a list is `all id`. + all (b: b)) + (map + (def: functionArgs def.value) + defs); + mergedFun = arg: anything.merge + (loc ++ [ "" ]) + (map (def: { + file = def.file; + value = def.value arg; + }) defs); + in + if args == {} + then mergedFun + else setFunctionArgs mergedFun args; # Otherwise fall back to only allowing all equal definitions }.${commonType} or mergeEqualOption; in mergeFunction loc defs;