Unverified Commit 44b831c0 authored by github-actions[bot]'s avatar github-actions[bot] Committed by GitHub
Browse files

Merge master into staging-next

parents ac86a854 cd519af9
Loading
Loading
Loading
Loading
+35 −5
Original line number Diff line number Diff line
@@ -751,17 +751,47 @@ let
          t' = opt.options.type;
          mergedType = t.typeMerge t'.functor;
          typesMergeable = mergedType != null;
          typeSet = if (bothHave "type") && typesMergeable
                       then { type = mergedType; }
                       else {};

          # TODO: Remove this when all downstream reliances of internals: 'functor.wrapped' are sufficiently migrated.
          # A function that adds the deprecated wrapped message to a type.
          addDeprecatedWrapped = t:
            t // {
              functor = t.functor // {
                wrapped = t.functor.wrappedDeprecationMessage {
                  inherit loc;
                };
              };
            };

          typeSet =
            if opt.options ? type then
              if res ? type then
                if typesMergeable then
                  {
                    type =
                      if mergedType ? functor.wrappedDeprecationMessage then
                        addDeprecatedWrapped mergedType
                      else
                        mergedType;
                  }
                else
                  # Keep in sync with the same error below!
                  throw "The option `${showOption loc}' in `${opt._file}' is already declared in ${showFiles res.declarations}."
              else if opt.options.type ? functor.wrappedDeprecationMessage then
                { type = addDeprecatedWrapped opt.options.type; }
              else
                {}
            else
              {};

          bothHave = k: opt.options ? ${k} && res ? ${k};
      in
      if bothHave "default" ||
         bothHave "example" ||
         bothHave "description" ||
         bothHave "apply" ||
         (bothHave "type" && (! typesMergeable))
         bothHave "apply"
      then
        # Keep in sync with the same error above!
        throw "The option `${showOption loc}' in `${opt._file}' is already declared in ${showFiles res.declarations}."
      else
        let
+9 −0
Original line number Diff line number Diff line
@@ -386,6 +386,14 @@ checkConfigOutput '^true$' config.conditionalWorks ./declare-attrsOf.nix ./attrs
checkConfigOutput '^false$' config.conditionalWorks ./declare-lazyAttrsOf.nix ./attrsOf-conditional-check.nix
checkConfigOutput '^"empty"$' config.value.foo ./declare-lazyAttrsOf.nix ./attrsOf-conditional-check.nix

# Check attrsWith type merging
checkConfigError 'The option `mergedLazyNonLazy'\'' in `.*'\'' is already declared in `.*'\''\.' options.mergedLazyNonLazy ./lazy-attrsWith.nix
checkConfigOutput '^11$' config.lazyResult ./lazy-attrsWith.nix
checkConfigError 'infinite recursion encountered' config.nonLazyResult ./lazy-attrsWith.nix

# Test the attrsOf functor.wrapped warning
# shellcheck disable=2016
NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `type.functor.wrapped` attribute of the option `mergedLazyLazy` is accessed, use `nestedTypes.elemType` instead.' options.mergedLazyLazy.type.functor.wrapped ./lazy-attrsWith.nix

# Even with multiple assignments, a type error should be thrown if any of them aren't valid
checkConfigError 'A definition for option .* is not of type .*' \
@@ -575,6 +583,7 @@ checkConfigOutput '^38|27$' options.submoduleLine38.declarationPositions.1.line
# nested options work
checkConfigOutput '^34$' options.nested.nestedLine34.declarationPositions.0.line ./declaration-positions.nix


cat <<EOF
====== module tests ======
$pass Pass
+57 −0
Original line number Diff line number Diff line
# Check that AttrsWith { lazy = true; } is lazy
{ lib, ... }:
let
  inherit (lib) types mkOption;

  lazyAttrsOf = mkOption {
    # Same as lazyAttrsOf
    type = types.attrsWith {
      lazy = true;
      elemType = types.int;
    };
  };

  attrsOf = mkOption {
    # Same as lazyAttrsOf
    type = types.attrsWith {
      elemType = types.int;
    };
  };
in
{
  imports = [
    #  Module A
    (
      { ... }:
      {
        options.mergedLazyLazy = lazyAttrsOf;
        options.mergedLazyNonLazy = lazyAttrsOf;
        options.mergedNonLazyNonLazy = attrsOf;
      }
    )
    # Module B
    (
      { ... }:
      {
        options.mergedLazyLazy = lazyAttrsOf;
        options.mergedLazyNonLazy = attrsOf;
        options.mergedNonLazyNonLazy = attrsOf;
      }
    )
    # Result
    (
      { config, ... }:
      {
        # Can only evaluate if lazy
        config.mergedLazyLazy.bar = config.mergedLazyLazy.baz + 1;
        config.mergedLazyLazy.baz = 10;
        options.lazyResult = mkOption { default = config.mergedLazyLazy.bar; };

        # Can not only evaluate if not lazy
        config.mergedNonLazyNonLazy.bar = config.mergedNonLazyNonLazy.baz + 1;
        config.mergedNonLazyNonLazy.baz = 10;
        options.nonLazyResult = mkOption { default = config.mergedNonLazyNonLazy.bar; };
      }
    )
  ];
}
+76 −42
Original line number Diff line number Diff line
@@ -83,11 +83,15 @@ rec {
  # Default type merging function
  # takes two type functors and return the merged type
  defaultTypeMerge = f: f':
    let mergedWrapped = f.wrapped.typeMerge f'.wrapped.functor;
    let
      mergedWrapped = f.wrapped.typeMerge f'.wrapped.functor;
      mergedPayload = f.binOp f.payload f'.payload;

      hasPayload = assert (f'.payload != null) == (f.payload != null); f.payload != null;
      hasWrapped = assert (f'.wrapped != null) == (f.wrapped != null); f.wrapped != null;

      typeFromPayload = if mergedPayload == null then null else f.type mergedPayload;
      typeFromWrapped = if mergedWrapped == null then null else f.type mergedWrapped;
    in
    # Abort early: cannot merge different types
    if f.name != f'.name
@@ -95,23 +99,23 @@ rec {
    else

    if hasPayload then
      if hasWrapped then
      # Just return the payload if returning wrapped is deprecated
      if f ? wrappedDeprecationMessage then
        typeFromPayload
      else if hasWrapped then
        # Has both wrapped and payload
        throw ''
          Type ${f.name} defines both `functor.payload` and `functor.wrapped` at the same time, which is not supported.

          Use either `functor.payload` or `functor.wrapped` but not both.

          If your code worked before remove `functor.payload` from the type definition.
          If your code worked before remove either `functor.wrapped` or `functor.payload` from the type definition.
        ''
      else
        # Has payload
        if mergedPayload == null then null else f.type mergedPayload
        typeFromPayload
    else
      if hasWrapped then
        # Has wrapped
        # TODO(@hsjobeki): This could also be a warning and removed in the future
        if mergedWrapped == null then null else f.type mergedWrapped
        typeFromWrapped
      else
        f.type;

@@ -582,48 +586,78 @@ rec {
        substSubModules = m: nonEmptyListOf (elemType.substSubModules m);
      };

    attrsOf = elemType: mkOptionType rec {
      name = "attrsOf";
      description = "attribute set of ${optionDescriptionPhrase (class: class == "noun" || class == "composite") elemType}";
      descriptionClass = "composite";
      check = isAttrs;
      merge = loc: defs:
        mapAttrs (n: v: v.value) (filterAttrs (n: v: v ? value) (zipAttrsWith (name: defs:
            (mergeDefinitions (loc ++ [name]) elemType defs).optionalValue
          )
          # Push down position info.
          (map (def: mapAttrs (n: v: { inherit (def) file; value = v; }) def.value) defs)));
      emptyValue = { value = {}; };
      getSubOptions = prefix: elemType.getSubOptions (prefix ++ ["<name>"]);
      getSubModules = elemType.getSubModules;
      substSubModules = m: attrsOf (elemType.substSubModules m);
      functor = (defaultFunctor name) // { wrapped = elemType; };
      nestedTypes.elemType = elemType;
    };
    attrsOf = elemType: attrsWith { inherit elemType; };

    # A version of attrsOf that's lazy in its values at the expense of
    # conditional definitions not working properly. E.g. defining a value with
    # `foo.attr = mkIf false 10`, then `foo ? attr == true`, whereas with
    # attrsOf it would correctly be `false`. Accessing `foo.attr` would throw an
    # error that it's not defined. Use only if conditional definitions don't make sense.
    lazyAttrsOf = elemType: mkOptionType rec {
      name = "lazyAttrsOf";
      description = "lazy attribute set of ${optionDescriptionPhrase (class: class == "noun" || class == "composite") elemType}";
    lazyAttrsOf = elemType: attrsWith { inherit elemType; lazy = true; };

    # base type for lazyAttrsOf and attrsOf
    attrsWith =
    let
      # Push down position info.
      pushPositions = map (def: mapAttrs (n: v: { inherit (def) file; value = v; }) def.value);
      binOp = lhs: rhs:
        let
          elemType = lhs.elemType.typeMerge rhs.elemType.functor;
          lazy =
            if lhs.lazy == rhs.lazy then
              lhs.lazy
            else
              null;
        in
        if elemType == null || lazy == null then
          null
        else
          {
            inherit elemType lazy;
          };
    in
    {
      elemType,
      lazy ? false,
    }:
    mkOptionType {
      name = if lazy then "lazyAttrsOf" else "attrsOf";
      description = (if lazy then "lazy attribute set" else "attribute set") + " of ${optionDescriptionPhrase (class: class == "noun" || class == "composite") elemType}";
      descriptionClass = "composite";
      check = isAttrs;
      merge = loc: defs:
      merge = if lazy then (
        # Lazy merge Function
        loc: defs:
          zipAttrsWith (name: defs:
            let merged = mergeDefinitions (loc ++ [name]) elemType defs;
            # mergedValue will trigger an appropriate error when accessed
            in merged.optionalValue.value or elemType.emptyValue.value or merged.mergedValue
          )
          # Push down position info.
        (map (def: mapAttrs (n: v: { inherit (def) file; value = v; }) def.value) defs);
          (pushPositions defs)
      ) else (
        # Non-lazy merge Function
        loc: defs:
          mapAttrs (n: v: v.value) (filterAttrs (n: v: v ? value) (zipAttrsWith (name: defs:
              (mergeDefinitions (loc ++ [name]) elemType (defs)).optionalValue
            )
          # Push down position info.
          (pushPositions defs)))
      );
      emptyValue = { value = {}; };
      getSubOptions = prefix: elemType.getSubOptions (prefix ++ ["<name>"]);
      getSubModules = elemType.getSubModules;
      substSubModules = m: lazyAttrsOf (elemType.substSubModules m);
      functor = (defaultFunctor name) // { wrapped = elemType; };
      substSubModules = m: attrsWith { elemType = elemType.substSubModules m; inherit lazy; };
      functor = defaultFunctor "attrsWith" // {
        wrappedDeprecationMessage = { loc }: lib.warn ''
          The deprecated `type.functor.wrapped` attribute of the option `${showOption loc}` is accessed, use `type.nestedTypes.elemType` instead.
        '' elemType;
        payload = {
          # Important!: Add new function attributes here in case of future changes
          inherit elemType lazy;
        };
        inherit binOp;
      };
      nestedTypes.elemType = elemType;
    };

+4 −0
Original line number Diff line number Diff line
@@ -55,6 +55,10 @@

- `gkraken` software and `hardware.gkraken.enable` option have been removed, use `coolercontrol` via `programs.coolercontrol.enable` option instead.

- `containerd` has been updated to v2, which contains breaking changes. See the [containerd
  2.0](https://github.com/containerd/containerd/blob/main/docs/containerd-2.0.md) documentation for more
  details.

- the notmuch vim plugin now lives in a separate output of the `notmuch`
  package. Installing `notmuch` will not bring the notmuch vim package anymore,
  add `vimPlugins.notmuch-vim` to your (Neo)vim configuration if you want the
Loading