summary refs log tree commit diff
diff options
context:
space:
mode:
-rwxr-xr-xlib/tests/modules.sh3
-rw-r--r--lib/tests/modules/adhoc-freeformType-survives-type-merge.nix14
-rw-r--r--lib/types.nix4
3 files changed, 20 insertions, 1 deletions
diff --git a/lib/tests/modules.sh b/lib/tests/modules.sh
index e4bb7ad2190..350fe85e748 100755
--- a/lib/tests/modules.sh
+++ b/lib/tests/modules.sh
@@ -311,6 +311,9 @@ checkConfigOutput '^"hello"$' config.theOption.str ./optionTypeMerging.nix
 # Test that types.optionType correctly annotates option locations
 checkConfigError 'The option .theOption.nested. in .other.nix. is already declared in .optionTypeFile.nix.' config.theOption.nested ./optionTypeFile.nix
 
+# Test that types.optionType leaves types untouched as long as they don't need to be merged
+checkConfigOutput 'ok' config.freeformItems.foo.bar ./adhoc-freeformType-survives-type-merge.nix
+
 cat <<EOF
 ====== module tests ======
 $pass Pass
diff --git a/lib/tests/modules/adhoc-freeformType-survives-type-merge.nix b/lib/tests/modules/adhoc-freeformType-survives-type-merge.nix
new file mode 100644
index 00000000000..3cefb543c25
--- /dev/null
+++ b/lib/tests/modules/adhoc-freeformType-survives-type-merge.nix
@@ -0,0 +1,14 @@
+{ lib, ... }: {
+  options.dummy = lib.mkOption { type = lib.types.anything; default = {}; };
+  freeformType =
+    let
+      a = lib.types.attrsOf (lib.types.submodule { options.bar = lib.mkOption { }; });
+    in
+    # modifying types like this breaks type merging.
+    # This test makes sure that type merging is not performed when only a single declaration exists.
+    # Don't modify types in practice!
+    a // {
+      merge = loc: defs: { freeformItems = a.merge loc defs; };
+    };
+  config.foo.bar = "ok";
+}
diff --git a/lib/types.nix b/lib/types.nix
index 3fcac9c31b3..2e7261f7553 100644
--- a/lib/types.nix
+++ b/lib/types.nix
@@ -535,7 +535,9 @@ rec {
       description = "optionType";
       check = value: value._type or null == "option-type";
       merge = loc: defs:
-        let
+        if length defs == 1
+        then (head defs).value
+        else let
           # Prepares the type definitions for mergeOptionDecls, which
           # annotates submodules types with file locations
           optionModules = map ({ value, file }: