Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 31 additions & 7 deletions lib/modules.nix
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
# This `let` must be top level
let
warn = builtins.warn or builtins.trace;

# Only emitted once (per evaluator instance, per Nixpkgs source version)
freeformBadTypeContext = warn ''
freeformType does not pass type check: This module evaluation contains one or more instances of a problem with freeformType. If you are not the maintainer of any module, ignore these messages about freeformType.
The module system's `freeformType` is matched to a whole attribute set, but due to a bug, some types were *not* checked if they were assigned directly as the `freeformType`, notably types derived from `either`.
This problem can generally be fixed by wrapping it as it should: `freeformType = attrsOf (<the intended value type>);`.
'' null;

in

{ lib }:

let
Expand Down Expand Up @@ -276,13 +289,24 @@ let

# If freeformType is set, this is for definitions that don't have an associated option
freeformConfig =
let
defs = map (def: {
file = def.file;
value = setAttrByPath def.prefix def.value;
}) merged.unmatchedDefns;
in
if defs == [ ] then { } else declaredConfig._module.freeformType.merge prefix defs;
if merged.unmatchedDefns == [ ] then
{ }
else
let
defs = map (def: {
file = def.file;
value = setAttrByPath def.prefix def.value;
}) merged.unmatchedDefns;
mergedFreeform = mergeDefinitions prefix declaredConfig._module.freeformType defs;
in
# TODO (after 25.11): make this throw or just use mergedFreeform.checkedAndMerged.value, which throws
warnIf (mergedFreeform.checkedAndMerged.headError != null)
(builtins.seq freeformBadTypeContext "freeformType at `${showOption prefix}` does not pass type check; see preceding message.")
(
# Use valueMeta to silence a warning from `either`, which is a more general but worse duplicate
mergedFreeform.checkedAndMerged.valueMeta._deprecatedFreeformTypeValueOverride
or mergedFreeform.checkedAndMerged.value
);

in
if declaredConfig._module.freeformType == null then
Expand Down
11 changes: 10 additions & 1 deletion lib/types.nix
Original file line number Diff line number Diff line change
Expand Up @@ -1449,11 +1449,20 @@ let
rec {
valueMeta = {
inherit headError;
# Allow freeformType to suppress the warning below, because it has a more specific solution.
_deprecatedFreeformTypeValueOverride = mergeOneOption loc defs;
};
headError = {
message = "The option `${showOption loc}` is neither a value of type `${t1.description}` nor `${t2.description}`, Definition values: ${showDefs defs}";
};
value = abort "(t.merge.v2 defs).value must only be accessed when `.headError == null`. This is a bug in code that consumes a module system type.";
value =
# TODO (after 25.11): Make this an error
lib.warn
"while accessing option ${showOption loc}: (t.merge.v2 defs).value must only be accessed when `.headError == null`. This is a bug in code that use the module system type interface incorrectly. This will be an error after Nixpkgs 25.11."
mergeOneOption
loc
defs;
#
};
in
checkedAndMerged;
Expand Down
Loading