diff --git a/lib/tests/misc.nix b/lib/tests/misc.nix index 498f9064d2ff..e86c366d4ce4 100644 --- a/lib/tests/misc.nix +++ b/lib/tests/misc.nix @@ -2521,6 +2521,21 @@ runTests { expr = (with types; either int (listOf (either bool str))).description; expected = "signed integer or list of (boolean or string)"; }; + testTypeFunctionToPropagateFunctionArgs = { + expr = lib.functionArgs ((types.functionTo types.null).merge [] [ + { + value = {a, b ? false, ... }: null; + } + { + value = {b, c ? false, ... }: null; + } + ]); + expected = { + a = false; + b = false; + c = true; + }; + }; # Meta testGetExe'Output = { diff --git a/lib/types.nix b/lib/types.nix index cda048fd10ca..cdba500ae07f 100644 --- a/lib/types.nix +++ b/lib/types.nix @@ -873,8 +873,13 @@ rec { description = "function that evaluates to a(n) ${optionDescriptionPhrase (class: class == "noun" || class == "composite") elemType}"; descriptionClass = "composite"; check = isFunction; - merge = loc: defs: - fnArgs: (mergeDefinitions (loc ++ [ "" ]) elemType (map (fn: { inherit (fn) file; value = fn.value fnArgs; }) defs)).mergedValue; + merge = loc: defs: { + # An argument attribute has a default when it has a default in all definitions + __functionArgs = lib.zipAttrsWith (_: lib.all (x: x)) ( + lib.map (fn: lib.functionArgs fn.value) defs + ); + __functor = _: callerArgs: (mergeDefinitions (loc ++ [ "" ]) elemType (map (fn: { inherit (fn) file; value = fn.value callerArgs; }) defs)).mergedValue; + }; getSubOptions = prefix: elemType.getSubOptions (prefix ++ [ "" ]); getSubModules = elemType.getSubModules; substSubModules = m: functionTo (elemType.substSubModules m);