From 3cf5ad01642c19688d5673175f551fa68926f3c3 Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Wed, 19 Nov 2025 23:55:48 +0100 Subject: [PATCH] libexpr/builtins: Document unsafeDiscardStringContext Co-authored-by: eldritch horrors Change-Id: I4ebbbc9d32152f296a2553f2fd324dacf8af02d8 --- .../builtins/unsafeDiscardStringContext.md | 33 +++++++++++++++++++ lix/libexpr/extra-primops.hh | 1 + lix/libexpr/meson.build | 1 + lix/libexpr/primops/context.cc | 9 +---- 4 files changed, 36 insertions(+), 8 deletions(-) create mode 100644 lix/libexpr/builtins/unsafeDiscardStringContext.md diff --git a/lix/libexpr/builtins/unsafeDiscardStringContext.md b/lix/libexpr/builtins/unsafeDiscardStringContext.md new file mode 100644 index 000000000..25cc91440 --- /dev/null +++ b/lix/libexpr/builtins/unsafeDiscardStringContext.md @@ -0,0 +1,33 @@ +--- +name: unsafeDiscardStringContext +args: [s] +--- + +Returns a copy of the string `s` with all string context associated with `s` removed. +Since string context is used for dependency tracking the returned string will also have +*no dependencies* on store objects, even when the original string `s` had such dependencies. + +This function is mainly useful when discarding dependencies is explicitly required, e.g. +to produce a string listing all inputs of a derivation without propagating these inputs +as dependencies into all *users* of the listing. For example, the derivation `dep` in the +following example will pull `hello` into its closure despite never using it while `nodep` +will not: + +```nix +dep = runCommand "dep" { + inherit hello; +} "echo hello is at $hello >$out"; + +nodep = runCommand "nodep" { + hello = builtins.unsafeDiscardStringContext hello; +} "echo hello is at $hello >$out"; +``` + +This behavior also makes this function unsafe: if `s` contains the path of a +store object that is not present in the store then any use of `s` in a +derivation tree will attempt to realize that path in the store, but no use +of `unsafeDiscardStringContext s` will. This can lead to derivation outputs that +refer to paths that were never created. + +Lix cannot determine whether such reference are safe or not and must pass +this obligation to the user. diff --git a/lix/libexpr/extra-primops.hh b/lix/libexpr/extra-primops.hh index fcaa5af7c..9fd753ad4 100644 --- a/lix/libexpr/extra-primops.hh +++ b/lix/libexpr/extra-primops.hh @@ -18,6 +18,7 @@ void prim_appendContext(EvalState & state, Value ** args, Value & v); void prim_getContext(EvalState & state, Value * * args, Value & v); void prim_hasContext(EvalState & state, Value * * args, Value & v); void prim_unsafeDiscardOutputDependency(EvalState & state, Value * * args, Value & v); +void prim_unsafeDiscardStringContext(EvalState & state, Value ** args, Value & v); namespace flake { diff --git a/lix/libexpr/meson.build b/lix/libexpr/meson.build index ddee2e0bb..4aa2ecddc 100644 --- a/lix/libexpr/meson.build +++ b/lix/libexpr/meson.build @@ -151,6 +151,7 @@ builtin_definitions = files( 'builtins/tryEval.md', 'builtins/typeOf.md', 'builtins/unsafeDiscardOutputDependency.md', + 'builtins/unsafeDiscardStringContext.md', 'builtins/unsafeGetAttrPos.md', 'builtins/warn.md', 'builtins/zipAttrsWith.md', diff --git a/lix/libexpr/primops/context.cc b/lix/libexpr/primops/context.cc index 2c6e2baa4..d7e23f3df 100644 --- a/lix/libexpr/primops/context.cc +++ b/lix/libexpr/primops/context.cc @@ -7,20 +7,13 @@ namespace nix { -static void prim_unsafeDiscardStringContext(EvalState & state, Value * * args, Value & v) +void prim_unsafeDiscardStringContext(EvalState & state, Value ** args, Value & v) { NixStringContext context; auto s = state.coerceToString(noPos, *args[0], context, "while evaluating the argument passed to builtins.unsafeDiscardStringContext"); v.mkString(*s); } -static RegisterPrimOp primop_unsafeDiscardStringContext({ - .name = "__unsafeDiscardStringContext", - .arity = 1, - .fun = prim_unsafeDiscardStringContext -}); - - void prim_hasContext(EvalState & state, Value * * args, Value & v) { NixStringContext context;