Skip to content

foo ? bar is not strict in its types, while hasAttr bar foo is #15768

@llakala

Description

@llakala

Describe the bug

I discovered this while looking at this expression in nixpkgs:

    if isAttrs licenses && licenses ? "licenseType" then
      !(lib.licenses.isFree licenses)
    else if isAttrs licenses then
      # ...

I was curious if the first isAttrs was even necessary here. The logic of ? is defined here:

nix/src/libexpr/eval.cc

Lines 1501 to 1521 in b744171

void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v)
{
Value vTmp;
Value * vAttrs = &vTmp;
e->eval(state, env, vTmp);
for (auto & i : attrPath) {
state.forceValue(*vAttrs, getPos());
const Attr * j;
auto name = getName(i, state, env);
if (vAttrs->type() == nAttrs && (j = vAttrs->attrs()->get(name))) {
vAttrs = j->value;
} else {
v.mkBool(false);
return;
}
}
v.mkBool(true);
}

As you can see, no typechecks are performed. If vAttrs isn't an attribute set, it just falls back to returning false. (Also, it seems to perform this typecheck on every attribute while iterating, rather than just once - I'd hope the compiler would catch that, but who knows, maybe that's a free performance win).

On the other hand, hasAttr forces its argument to be an attrset:

nix/src/libexpr/primops.cc

Lines 3218 to 3223 in b744171

static void prim_hasAttr(EvalState & state, const PosIdx pos, Value ** args, Value & v)
{
auto attr = state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.hasAttr");
state.forceAttrs(*args[1], pos, "while evaluating the second argument passed to builtins.hasAttr");
v.mkBool(args[1]->attrs()->get(state.symbols.create(attr)));
}

Steps To Reproduce

  • Enter nix repl
  • Observe that null ? example passes fine, while hasAttr "example" null throws an error

Expected behavior

Since the documentation describes hasAttr as "a dynamic version of the ? operator", it'd be nice if their behavior was aligned, or at least documented.

Metadata

❯ nix-env --version
warning: unknown setting 'repl-overlays'
nix-env (Nix) 2.34.6

Additional context

Checklist


Add 👍 to issues you find important.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions