diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml
index ec7ab4516..dd481160f 100644
--- a/.github/workflows/backport.yml
+++ b/.github/workflows/backport.yml
@@ -8,7 +8,7 @@ jobs:
if: github.repository_owner == 'NixOS' && github.event.pull_request.merged == true && (github.event_name != 'labeled' || startsWith('backport', github.event.label.name))
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha }}
# required to find all branches
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 09436b7e3..d01ef4768 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -14,10 +14,10 @@ jobs:
runs-on: ${{ matrix.os }}
timeout-minutes: 60
steps:
- - uses: actions/checkout@v2.4.0
+ - uses: actions/checkout@v3
with:
fetch-depth: 0
- - uses: cachix/install-nix-action@v16
+ - uses: cachix/install-nix-action@v17
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
- uses: cachix/cachix-action@v10
if: needs.check_cachix.outputs.secret == 'true'
@@ -46,11 +46,11 @@ jobs:
outputs:
installerURL: ${{ steps.prepare-installer.outputs.installerURL }}
steps:
- - uses: actions/checkout@v2.4.0
+ - uses: actions/checkout@v3
with:
fetch-depth: 0
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
- - uses: cachix/install-nix-action@v16
+ - uses: cachix/install-nix-action@v17
- uses: cachix/cachix-action@v10
with:
name: '${{ env.CACHIX_NAME }}'
@@ -67,9 +67,9 @@ jobs:
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- - uses: actions/checkout@v2.4.0
+ - uses: actions/checkout@v3
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
- - uses: cachix/install-nix-action@v16
+ - uses: cachix/install-nix-action@v17
with:
install_url: '${{needs.installer.outputs.installerURL}}'
install_options: "--tarball-url-prefix https://${{ env.CACHIX_NAME }}.cachix.org/serve"
@@ -83,10 +83,10 @@ jobs:
needs.check_cachix.outputs.secret == 'true'
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2.4.0
+ - uses: actions/checkout@v3
with:
fetch-depth: 0
- - uses: cachix/install-nix-action@v16
+ - uses: cachix/install-nix-action@v17
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
- run: echo NIX_VERSION="$(nix-instantiate --eval -E '(import ./default.nix).defaultPackage.${builtins.currentSystem}.version' | tr -d \")" >> $GITHUB_ENV
- uses: cachix/cachix-action@v10
diff --git a/.github/workflows/hydra_status.yml b/.github/workflows/hydra_status.yml
index b97076bd7..53e69cb2d 100644
--- a/.github/workflows/hydra_status.yml
+++ b/.github/workflows/hydra_status.yml
@@ -9,7 +9,7 @@ jobs:
if: github.repository_owner == 'NixOS'
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2.4.0
+ - uses: actions/checkout@v3
with:
fetch-depth: 0
- run: bash scripts/check-hydra-status.sh
diff --git a/.gitignore b/.gitignore
index 4b290425a..db363aefd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -79,6 +79,7 @@ perl/Makefile.config
/tests/shell.drv
/tests/config.nix
/tests/ca/config.nix
+/tests/repl-result-out
# /tests/lang/
/tests/lang/*.out
@@ -90,6 +91,7 @@ perl/Makefile.config
/misc/systemd/nix-daemon.service
/misc/systemd/nix-daemon.socket
+/misc/systemd/nix-daemon.conf
/misc/upstart/nix-daemon.conf
/src/resolve-system-dependencies/resolve-system-dependencies
diff --git a/.version b/.version
index 6533b6687..f3ac133c5 100644
--- a/.version
+++ b/.version
@@ -1 +1 @@
-2.8.0
\ No newline at end of file
+2.9.0
\ No newline at end of file
diff --git a/doc/manual/generate-options.nix b/doc/manual/generate-options.nix
index 84d90beb6..2d586fa1b 100644
--- a/doc/manual/generate-options.nix
+++ b/doc/manual/generate-options.nix
@@ -6,7 +6,8 @@ options:
concatStrings (map
(name:
let option = options.${name}; in
- " - `${name}` \n\n"
+ " - [`${name}`](#conf-${name})"
+ + "
\n\n"
+ concatStrings (map (s: " ${s}\n") (splitLines option.description)) + "\n\n"
+ (if option.documentDefault
then " **Default:** " + (
diff --git a/doc/manual/src/SUMMARY.md.in b/doc/manual/src/SUMMARY.md.in
index f0f9457d2..860222337 100644
--- a/doc/manual/src/SUMMARY.md.in
+++ b/doc/manual/src/SUMMARY.md.in
@@ -72,6 +72,7 @@
- [CLI guideline](contributing/cli-guideline.md)
- [Release Notes](release-notes/release-notes.md)
- [Release X.Y (202?-??-??)](release-notes/rl-next.md)
+ - [Release 2.8 (2022-04-19)](release-notes/rl-2.8.md)
- [Release 2.7 (2022-03-07)](release-notes/rl-2.7.md)
- [Release 2.6 (2022-01-24)](release-notes/rl-2.6.md)
- [Release 2.5 (2021-12-13)](release-notes/rl-2.5.md)
diff --git a/doc/manual/src/installation/installing-binary.md b/doc/manual/src/installation/installing-binary.md
index 4367654a2..e5fb50088 100644
--- a/doc/manual/src/installation/installing-binary.md
+++ b/doc/manual/src/installation/installing-binary.md
@@ -84,7 +84,9 @@ The installer will modify `/etc/bashrc`, and `/etc/zshrc` if they exist.
The installer will first back up these files with a `.backup-before-nix`
extension. The installer will also create `/etc/profile.d/nix.sh`.
-You can uninstall Nix with the following commands:
+## Uninstalling
+
+### Linux
```console
sudo rm -rf /etc/profile/nix.sh /etc/nix /nix ~root/.nix-profile ~root/.nix-defexpr ~root/.nix-channels ~/.nix-profile ~/.nix-defexpr ~/.nix-channels
@@ -95,15 +97,95 @@ sudo systemctl stop nix-daemon.service
sudo systemctl disable nix-daemon.socket
sudo systemctl disable nix-daemon.service
sudo systemctl daemon-reload
-
-# If you are on macOS, you will need to run:
-sudo launchctl unload /Library/LaunchDaemons/org.nixos.nix-daemon.plist
-sudo rm /Library/LaunchDaemons/org.nixos.nix-daemon.plist
```
There may also be references to Nix in `/etc/profile`, `/etc/bashrc`,
and `/etc/zshrc` which you may remove.
+### macOS
+
+1. Edit `/etc/zshrc` and `/etc/bashrc` to remove the lines sourcing
+ `nix-daemon.sh`, which should look like this:
+
+ ```bash
+ # Nix
+ if [ -e '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh' ]; then
+ . '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh'
+ fi
+ # End Nix
+ ```
+
+ If these files haven't been altered since installing Nix you can simply put
+ the backups back in place:
+
+ ```console
+ sudo mv /etc/zshrc.backup-before-nix /etc/zshrc
+ sudo mv /etc/bashrc.backup-before-nix /etc/bashrc
+ ```
+
+ This will stop shells from sourcing the file and bringing everything you
+ installed using Nix in scope.
+
+2. Stop and remove the Nix daemon services:
+
+ ```console
+ sudo launchctl unload /Library/LaunchDaemons/org.nixos.nix-daemon.plist
+ sudo rm /Library/LaunchDaemons/org.nixos.nix-daemon.plist
+ sudo launchctl unload /Library/LaunchDaemons/org.nixos.darwin-store.plist
+ sudo rm /Library/LaunchDaemons/org.nixos.darwin-store.plist
+ ```
+
+ This stops the Nix daemon and prevents it from being started next time you
+ boot the system.
+
+3. Remove the `nixbld` group and the `_nixbuildN` users:
+
+ ```console
+ sudo dscl . -delete /Groups/nixbld
+ for u in $(sudo dscl . -list /Users | grep _nixbld); do sudo dscl . -delete /Users/$u; done
+ ```
+
+ This will remove all the build users that no longer serve a purpose.
+
+4. Edit fstab using `sudo vifs` to remove the line mounting the Nix Store
+ volume on `/nix`, which looks like this,
+ `LABEL=Nix\040Store /nix apfs rw,nobrowse`. This will prevent automatic
+ mounting of the Nix Store volume.
+
+5. Edit `/etc/synthetic.conf` to remove the `nix` line. If this is the only
+ line in the file you can remove it entirely, `sudo rm /etc/synthetic.conf`.
+ This will prevent the creation of the empty `/nix` directory to provide a
+ mountpoint for the Nix Store volume.
+
+6. Remove the files Nix added to your system:
+
+ ```console
+ sudo rm -rf /etc/nix /var/root/.nix-profile /var/root/.nix-defexpr /var/root/.nix-channels ~/.nix-profile ~/.nix-defexpr ~/.nix-channels
+ ```
+
+ This gets rid of any data Nix may have created except for the store which is
+ removed next.
+
+7. Remove the Nix Store volume:
+
+ ```console
+ sudo diskutil apfs deleteVolume /nix
+ ```
+
+ This will remove the Nix Store volume and everything that was added to the
+ store.
+
+> **Note**
+>
+> After you complete the steps here, you will still have an empty `/nix`
+> directory. This is an expected sign of a successful uninstall. The empty
+> `/nix` directory will disappear the next time you reboot.
+>
+> You do not have to reboot to finish uninstalling Nix. The uninstall is
+> complete. macOS (Catalina+) directly controls root directories and its
+> read-only root will prevent you from manually deleting the empty `/nix`
+> mountpoint.
+
# macOS Installation
diff --git a/doc/manual/src/release-notes/rl-2.7.md b/doc/manual/src/release-notes/rl-2.7.md
index dc92a9cde..2f3879422 100644
--- a/doc/manual/src/release-notes/rl-2.7.md
+++ b/doc/manual/src/release-notes/rl-2.7.md
@@ -1,4 +1,4 @@
-# Release X.Y (2022-03-07)
+# Release 2.7 (2022-03-07)
* Nix will now make some helpful suggestions when you mistype
something on the command line. For instance, if you type `nix build
diff --git a/doc/manual/src/release-notes/rl-2.8.md b/doc/manual/src/release-notes/rl-2.8.md
new file mode 100644
index 000000000..9778e8c3a
--- /dev/null
+++ b/doc/manual/src/release-notes/rl-2.8.md
@@ -0,0 +1,53 @@
+# Release 2.8 (2022-04-19)
+
+* New experimental command: `nix fmt`, which applies a formatter
+ defined by the `formatter.` flake output to the Nix
+ expressions in a flake.
+
+* Various Nix commands can now read expressions from standard input
+ using `--file -`.
+
+* New experimental builtin function `builtins.fetchClosure` that
+ copies a closure from a binary cache at evaluation time and rewrites
+ it to content-addressed form (if it isn't already). Like
+ `builtins.storePath`, this allows importing pre-built store paths;
+ the difference is that it doesn't require the user to configure
+ binary caches and trusted public keys.
+
+ This function is only available if you enable the experimental
+ feature `fetch-closure`.
+
+* New experimental feature: *impure derivations*. These are
+ derivations that can produce a different result every time they're
+ built. Here is an example:
+
+ ```nix
+ stdenv.mkDerivation {
+ name = "impure";
+ __impure = true; # marks this derivation as impure
+ buildCommand = "date > $out";
+ }
+ ```
+
+ Running `nix build` twice on this expression will build the
+ derivation twice, producing two different content-addressed store
+ paths. Like fixed-output derivations, impure derivations have access
+ to the network. Only fixed-output derivations and impure derivations
+ can depend on an impure derivation.
+
+* `nix store make-content-addressable` has been renamed to `nix store
+ make-content-addressed`.
+
+* The `nixosModule` flake output attribute has been renamed consistent
+ with the `.default` renames in Nix 2.7.
+
+ * `nixosModule` → `nixosModules.default`
+
+ As before, the old output will continue to work, but `nix flake check` will
+ issue a warning about it.
+
+* `nix run` is now stricter in what it accepts: members of the `apps`
+ flake output are now required to be apps (as defined in [the
+ manual](https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-run.html#apps)),
+ and members of `packages` or `legacyPackages` must be derivations
+ (not apps).
diff --git a/doc/manual/src/release-notes/rl-next.md b/doc/manual/src/release-notes/rl-next.md
index c9753f9aa..7b3ad4e58 100644
--- a/doc/manual/src/release-notes/rl-next.md
+++ b/doc/manual/src/release-notes/rl-next.md
@@ -1,3 +1,16 @@
# Release X.Y (202?-??-??)
-* Various nix commands can now read expressions from stdin with `--file -`.
+* Nix now provides better integration with zsh's run-help feature. It is now
+ included in the Nix installation in the form of an autoloadable shell
+ function, run-help-nix. It picks up Nix subcommands from the currently typed
+ in command and directs the user to the associated man pages.
+
+* `nix repl` has a new build-'n-link (`:bl`) command that builds a derivation
+ while creating GC root symlinks.
+
+* The path produced by `builtins.toFile` is now allowed to be imported or read
+ even with restricted evaluation. Note that this will not work with a
+ read-only store.
+
+* `nix build` has a new `--print-out-paths` flag to print the resulting output paths.
+ This matches the default behaviour of `nix-build`.
diff --git a/docker.nix b/docker.nix
index 251bd2f46..0cd64856f 100644
--- a/docker.nix
+++ b/docker.nix
@@ -22,6 +22,7 @@ let
findutils
iana-etc
git
+ openssh
];
users = {
diff --git a/flake.lock b/flake.lock
index 61eccb73c..cd79fa85e 100644
--- a/flake.lock
+++ b/flake.lock
@@ -18,11 +18,11 @@
},
"nixpkgs": {
"locked": {
- "lastModified": 1632864508,
- "narHash": "sha256-d127FIvGR41XbVRDPVvozUPQ/uRHbHwvfyKHwEt5xFM=",
+ "lastModified": 1645296114,
+ "narHash": "sha256-y53N7TyIkXsjMpOG7RhvqJFGDacLs9HlyHeSTBioqYU=",
"owner": "NixOS",
"repo": "nixpkgs",
- "rev": "82891b5e2c2359d7e58d08849e4c89511ab94234",
+ "rev": "530a53dcbc9437363471167a5e4762c5fcfa34a1",
"type": "github"
},
"original": {
diff --git a/misc/bash/completion.sh b/misc/bash/completion.sh
index 045053dee..9af695f5a 100644
--- a/misc/bash/completion.sh
+++ b/misc/bash/completion.sh
@@ -15,7 +15,7 @@ function _complete_nix {
else
COMPREPLY+=("$completion")
fi
- done < <(NIX_GET_COMPLETIONS=$cword "${words[@]/#\~/$HOME}" 2>/dev/null)
+ done < <(NIX_GET_COMPLETIONS=$cword "${words[@]}" 2>/dev/null)
__ltrim_colon_completions "$cur"
}
diff --git a/misc/systemd/local.mk b/misc/systemd/local.mk
index 1fa037485..76121a0f9 100644
--- a/misc/systemd/local.mk
+++ b/misc/systemd/local.mk
@@ -1,7 +1,8 @@
ifdef HOST_LINUX
$(foreach n, nix-daemon.socket nix-daemon.service, $(eval $(call install-file-in, $(d)/$(n), $(prefix)/lib/systemd/system, 0644)))
+ $(foreach n, nix-daemon.conf, $(eval $(call install-file-in, $(d)/$(n), $(prefix)/lib/tmpfiles.d, 0644)))
- clean-files += $(d)/nix-daemon.socket $(d)/nix-daemon.service
+ clean-files += $(d)/nix-daemon.socket $(d)/nix-daemon.service $(d)/nix-daemon.conf
endif
diff --git a/misc/systemd/nix-daemon.conf.in b/misc/systemd/nix-daemon.conf.in
new file mode 100644
index 000000000..e7b264234
--- /dev/null
+++ b/misc/systemd/nix-daemon.conf.in
@@ -0,0 +1 @@
+d @localstatedir@/nix/daemon-socket 0755 root root - -
diff --git a/misc/systemd/nix-daemon.service.in b/misc/systemd/nix-daemon.service.in
index b4badf2ba..24d894898 100644
--- a/misc/systemd/nix-daemon.service.in
+++ b/misc/systemd/nix-daemon.service.in
@@ -3,6 +3,7 @@ Description=Nix Daemon
Documentation=man:nix-daemon https://nixos.org/manual
RequiresMountsFor=@storedir@
RequiresMountsFor=@localstatedir@
+RequiresMountsFor=@localstatedir@/nix/db
ConditionPathIsReadWrite=@localstatedir@/nix/daemon-socket
[Service]
diff --git a/misc/zsh/local.mk b/misc/zsh/local.mk
index 418fb1377..0b4e294fb 100644
--- a/misc/zsh/local.mk
+++ b/misc/zsh/local.mk
@@ -1 +1,2 @@
$(eval $(call install-file-as, $(d)/completion.zsh, $(datarootdir)/zsh/site-functions/_nix, 0644))
+$(eval $(call install-file-as, $(d)/run-help-nix, $(datarootdir)/zsh/site-functions/run-help-nix, 0644))
diff --git a/misc/zsh/run-help-nix b/misc/zsh/run-help-nix
new file mode 100644
index 000000000..f91a304eb
--- /dev/null
+++ b/misc/zsh/run-help-nix
@@ -0,0 +1,42 @@
+emulate -L zsh
+
+# run-help is a zsh widget that can be bound to a key. It mainly looks up the
+# man page for the currently typed in command.
+#
+# Although run-help works for any command without requiring special support,
+# it can only deduce the right man page based solely on the name of the
+# command. Programs like Nix provide better integration with run-help by
+# helping zsh identify Nix subcommands and their corresponding man pages. This
+# is what this function does.
+#
+# To actually use run-help on zsh, place the following lines in your .zshrc:
+#
+# (( $+aliases[run-help] )) && unalias run-help
+# autoload -Uz run-help run-help-nix
+#
+# Then also assign run-help to any key of choice:
+#
+# bindkey '^[h' run-help
+
+while [[ "$#" != 0 && "$1" == -* ]]; do
+ shift
+done
+
+local -a subcommands; subcommands=( nix3 )
+
+local arg
+for arg in "$@"; do
+ if man -w "${(j:-:)subcommands}-$arg" >/dev/null 2>&1; then
+ subcommands+="$arg"
+ else
+ break
+ fi
+done
+
+if (( $#subcommands > 1 )); then
+ man "${(j:-:)subcommands}"
+else
+ man nix
+fi
+
+return $?
diff --git a/scripts/check-hydra-status.sh b/scripts/check-hydra-status.sh
index 5e2f03429..e62705e94 100644
--- a/scripts/check-hydra-status.sh
+++ b/scripts/check-hydra-status.sh
@@ -14,7 +14,7 @@ curl -sS -H 'Accept: application/json' https://hydra.nixos.org/jobset/nix/master
someBuildFailed=0
for buildId in $BUILDS_FOR_LATEST_EVAL; do
- buildInfo=$(curl -sS -H 'Accept: application/json' "https://hydra.nixos.org/build/$buildId")
+ buildInfo=$(curl --fail -sS -H 'Accept: application/json' "https://hydra.nixos.org/build/$buildId")
finished=$(echo "$buildInfo" | jq -r '.finished')
diff --git a/scripts/install-multi-user.sh b/scripts/install-multi-user.sh
index 5f2c93b7f..b79a9c23a 100644
--- a/scripts/install-multi-user.sh
+++ b/scripts/install-multi-user.sh
@@ -423,6 +423,18 @@ EOF
fi
done
+ if [ "$(uname -s)" = "Linux" ] && [ ! -e /run/systemd/system ]; then
+ warning < /dev/null 2>&1; then
- fetch() { curl -L "$1" -o "$2"; }
+ fetch() { curl --fail -L "$1" -o "$2"; }
elif command -v wget > /dev/null 2>&1; then
fetch() { wget "$1" -O "$2"; }
else
diff --git a/src/build-remote/build-remote.cc b/src/build-remote/build-remote.cc
index 8c9133c17..ff8ba2724 100644
--- a/src/build-remote/build-remote.cc
+++ b/src/build-remote/build-remote.cc
@@ -300,7 +300,7 @@ connected:
std::set missingRealisations;
StorePathSet missingPaths;
- if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations) && !derivationHasKnownOutputPaths(drv.type())) {
+ if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations) && !drv.type().hasKnownOutputPaths()) {
for (auto & outputName : wantedOutputs) {
auto thisOutputHash = outputHashes.at(outputName);
auto thisOutputId = DrvOutput{ thisOutputHash, outputName };
diff --git a/src/libcmd/command.cc b/src/libcmd/command.cc
index dc8fa9e5a..f28cfe5de 100644
--- a/src/libcmd/command.cc
+++ b/src/libcmd/command.cc
@@ -197,16 +197,17 @@ void StorePathCommand::run(ref store, std::vector && storePath
run(store, *storePaths.begin());
}
-Strings editorFor(const Pos & pos)
+Strings editorFor(const Path & file, uint32_t line)
{
auto editor = getEnv("EDITOR").value_or("cat");
auto args = tokenizeString(editor);
- if (pos.line > 0 && (
+ if (line > 0 && (
editor.find("emacs") != std::string::npos ||
editor.find("nano") != std::string::npos ||
- editor.find("vim") != std::string::npos))
- args.push_back(fmt("+%d", pos.line));
- args.push_back(pos.file);
+ editor.find("vim") != std::string::npos ||
+ editor.find("kak") != std::string::npos))
+ args.push_back(fmt("+%d", line));
+ args.push_back(file);
return args;
}
diff --git a/src/libcmd/command.hh b/src/libcmd/command.hh
index 0f6125f11..078e2a2ce 100644
--- a/src/libcmd/command.hh
+++ b/src/libcmd/command.hh
@@ -85,11 +85,12 @@ struct SourceExprCommand : virtual Args, MixFlakeOptions
{
std::optional file;
std::optional expr;
+ bool readOnlyMode = false;
// FIXME: move this; not all commands (e.g. 'nix run') use it.
OperateOn operateOn = OperateOn::Output;
- SourceExprCommand();
+ SourceExprCommand(bool supportReadOnlyMode = false);
std::vector> parseInstallables(
ref store, std::vector ss);
@@ -128,13 +129,13 @@ struct InstallableCommand : virtual Args, SourceExprCommand
{
std::shared_ptr installable;
- InstallableCommand();
+ InstallableCommand(bool supportReadOnlyMode = false);
void prepare() override;
std::optional getFlakeRefForCompletion() override
{
- return parseFlakeRef(_installable, absPath("."));
+ return parseFlakeRefWithFragment(_installable, absPath(".")).first;
}
private:
@@ -218,7 +219,7 @@ static RegisterCommand registerCommand2(std::vector && name)
/* Helper function to generate args that invoke $EDITOR on
filename:lineno. */
-Strings editorFor(const Pos & pos);
+Strings editorFor(const Path & file, uint32_t line);
struct MixProfile : virtual StoreCommand
{
diff --git a/src/libexpr/common-eval-args.cc b/src/libcmd/common-eval-args.cc
similarity index 95%
rename from src/libexpr/common-eval-args.cc
rename to src/libcmd/common-eval-args.cc
index e50ff244c..5b6e82388 100644
--- a/src/libexpr/common-eval-args.cc
+++ b/src/libcmd/common-eval-args.cc
@@ -7,6 +7,7 @@
#include "registry.hh"
#include "flake/flakeref.hh"
#include "store-api.hh"
+#include "command.hh"
namespace nix {
@@ -59,6 +60,9 @@ MixEvalArgs::MixEvalArgs()
fetchers::Attrs extraAttrs;
if (to.subdir != "") extraAttrs["dir"] = to.subdir;
fetchers::overrideRegistry(from.input, to.input, extraAttrs);
+ }},
+ .completer = {[&](size_t, std::string_view prefix) {
+ completeFlakeRef(openStore(), prefix);
}}
});
diff --git a/src/libexpr/common-eval-args.hh b/src/libcmd/common-eval-args.hh
similarity index 100%
rename from src/libexpr/common-eval-args.hh
rename to src/libcmd/common-eval-args.hh
diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc
index 2f8283fd5..9f5eec2b5 100644
--- a/src/libcmd/installables.cc
+++ b/src/libcmd/installables.cc
@@ -1,4 +1,6 @@
+#include "globals.hh"
#include "installables.hh"
+#include "util.hh"
#include "command.hh"
#include "attr-path.hh"
#include "common-eval-args.hh"
@@ -99,6 +101,14 @@ MixFlakeOptions::MixFlakeOptions()
lockFlags.inputOverrides.insert_or_assign(
flake::parseInputPath(inputPath),
parseFlakeRef(flakeRef, absPath("."), true));
+ }},
+ .completer = {[&](size_t n, std::string_view prefix) {
+ if (n == 0) {
+ if (auto flakeRef = getFlakeRefForCompletion())
+ completeFlakeInputPath(getEvalState(), *flakeRef, prefix);
+ } else if (n == 1) {
+ completeFlakeRef(getEvalState()->store, prefix);
+ }
}}
});
@@ -129,7 +139,7 @@ MixFlakeOptions::MixFlakeOptions()
});
}
-SourceExprCommand::SourceExprCommand()
+SourceExprCommand::SourceExprCommand(bool supportReadOnlyMode)
{
addFlag({
.longName = "file",
@@ -157,6 +167,17 @@ SourceExprCommand::SourceExprCommand()
.category = installablesCategory,
.handler = {&operateOn, OperateOn::Derivation},
});
+
+ if (supportReadOnlyMode) {
+ addFlag({
+ .longName = "read-only",
+ .description =
+ "Do not instantiate each evaluated derivation. "
+ "This improves performance, but can cause errors when accessing "
+ "store paths of derivations during evaluation.",
+ .handler = {&readOnlyMode, true},
+ });
+ }
}
Strings SourceExprCommand::getDefaultFlakeAttrPaths()
@@ -182,6 +203,8 @@ Strings SourceExprCommand::getDefaultFlakeAttrPathPrefixes()
void SourceExprCommand::completeInstallable(std::string_view prefix)
{
if (file) {
+ completionType = ctAttrs;
+
evalSettings.pureEval = false;
auto state = getEvalState();
Expr *e = state->parseExprFromFile(
@@ -210,13 +233,14 @@ void SourceExprCommand::completeInstallable(std::string_view prefix)
Value v2;
state->autoCallFunction(*autoArgs, v1, v2);
- completionType = ctAttrs;
-
if (v2.type() == nAttrs) {
for (auto & i : *v2.attrs) {
- std::string name = i.name;
+ std::string name = state->symbols[i.name];
if (name.find(searchWord) == 0) {
- completions->add(i.name);
+ if (prefix_ == "")
+ completions->add(name);
+ else
+ completions->add(prefix_ + "." + name);
}
}
}
@@ -244,10 +268,11 @@ void completeFlakeRefWithFragment(
if (hash == std::string::npos) {
completeFlakeRef(evalState->store, prefix);
} else {
+ completionType = ctAttrs;
+
auto fragment = prefix.substr(hash + 1);
auto flakeRefS = std::string(prefix.substr(0, hash));
- // FIXME: do tilde expansion.
- auto flakeRef = parseFlakeRef(flakeRefS, absPath("."));
+ auto flakeRef = parseFlakeRef(expandTilde(flakeRefS), absPath("."));
auto evalCache = openEvalCache(*evalState,
std::make_shared(lockFlake(*evalState, flakeRef, lockFlags)));
@@ -259,8 +284,6 @@ void completeFlakeRefWithFragment(
flake. */
attrPathPrefixes.push_back("");
- completionType = ctAttrs;
-
for (auto & attrPathPrefixS : attrPathPrefixes) {
auto attrPathPrefix = parseAttrPath(*evalState, attrPathPrefixS);
auto attrPathS = attrPathPrefixS + std::string(fragment);
@@ -268,7 +291,7 @@ void completeFlakeRefWithFragment(
std::string lastAttr;
if (!attrPath.empty() && !hasSuffix(attrPathS, ".")) {
- lastAttr = attrPath.back();
+ lastAttr = evalState->symbols[attrPath.back()];
attrPath.pop_back();
}
@@ -276,11 +299,11 @@ void completeFlakeRefWithFragment(
if (!attr) continue;
for (auto & attr2 : (*attr)->getAttrs()) {
- if (hasPrefix(attr2, lastAttr)) {
+ if (hasPrefix(evalState->symbols[attr2], lastAttr)) {
auto attrPath2 = (*attr)->getAttrPath(attr2);
/* Strip the attrpath prefix. */
attrPath2.erase(attrPath2.begin(), attrPath2.begin() + attrPathPrefix.size());
- completions->add(flakeRefS + "#" + concatStringsSep(".", attrPath2));
+ completions->add(flakeRefS + "#" + concatStringsSep(".", evalState->symbols.resolve(attrPath2)));
}
}
}
@@ -334,16 +357,16 @@ DerivedPath Installable::toDerivedPath()
return std::move(buildables[0]);
}
-std::vector, std::string>>
+std::vector[>
Installable::getCursors(EvalState & state)
{
auto evalCache =
std::make_shared(std::nullopt, state,
[&]() { return toValue(state).first; });
- return {{evalCache->getRoot(), ""}};
+ return {evalCache->getRoot()};
}
-std::pair, std::string>
+ref
Installable::getCursor(EvalState & state)
{
auto cursors = getCursors(state);
@@ -450,7 +473,7 @@ struct InstallableAttrPath : InstallableValue
std::string what() const override { return attrPath; }
- std::pair toValue(EvalState & state) override
+ std::pair toValue(EvalState & state) override
{
auto [vRes, pos] = findAlongAttrPath(state, attrPath, *cmd.getAutoArgs(state), **v);
state.forceValue(*vRes, pos);
@@ -566,43 +589,21 @@ InstallableFlake::InstallableFlake(
std::tuple InstallableFlake::toDerivation()
{
- auto lockedFlake = getLockedFlake();
+ auto attr = getCursor(*state);
- auto cache = openEvalCache(*state, lockedFlake);
- auto root = cache->getRoot();
+ auto attrPath = attr->getAttrPathStr();
- Suggestions suggestions;
+ if (!attr->isDerivation())
+ throw Error("flake output attribute '%s' is not a derivation", attrPath);
- for (auto & attrPath : getActualAttrPaths()) {
- debug("trying flake output attribute '%s'", attrPath);
+ auto drvPath = attr->forceDerivation();
- auto attrOrSuggestions = root->findAlongAttrPath(
- parseAttrPath(*state, attrPath),
- true
- );
+ auto drvInfo = DerivationInfo {
+ std::move(drvPath),
+ attr->getAttr(state->sOutputName)->getString()
+ };
- if (!attrOrSuggestions) {
- suggestions += attrOrSuggestions.getSuggestions();
- continue;
- }
-
- auto attr = *attrOrSuggestions;
-
- if (!attr->isDerivation())
- throw Error("flake output attribute '%s' is not a derivation", attrPath);
-
- auto drvPath = attr->forceDerivation();
-
- auto drvInfo = DerivationInfo {
- std::move(drvPath),
- attr->getAttr(state->sOutputName)->getString()
- };
-
- return {attrPath, lockedFlake->flake.lockedRef, std::move(drvInfo)};
- }
-
- throw Error(suggestions, "flake '%s' does not provide attribute %s",
- flakeRef, showAttrPaths(getActualAttrPaths()));
+ return {attrPath, getLockedFlake()->flake.lockedRef, std::move(drvInfo)};
}
std::vector InstallableFlake::toDerivations()
@@ -612,35 +613,12 @@ std::vector InstallableFlake::toDerivations()
return res;
}
-std::pair InstallableFlake::toValue(EvalState & state)
+std::pair InstallableFlake::toValue(EvalState & state)
{
- auto lockedFlake = getLockedFlake();
-
- auto vOutputs = getFlakeOutputs(state, *lockedFlake);
-
- auto emptyArgs = state.allocBindings(0);
-
- Suggestions suggestions;
-
- for (auto & attrPath : getActualAttrPaths()) {
- try {
- auto [v, pos] = findAlongAttrPath(state, attrPath, *emptyArgs, *vOutputs);
- state.forceValue(*v, pos);
- return {v, pos};
- } catch (AttrPathNotFound & e) {
- suggestions += e.info().suggestions;
- }
- }
-
- throw Error(
- suggestions,
- "flake '%s' does not provide attribute %s",
- flakeRef,
- showAttrPaths(getActualAttrPaths())
- );
+ return {&getCursor(state)->forceValue(), noPos};
}
-std::vector, std::string>>
+std::vector][>
InstallableFlake::getCursors(EvalState & state)
{
auto evalCache = openEvalCache(state,
@@ -648,21 +626,55 @@ InstallableFlake::getCursors(EvalState & state)
auto root = evalCache->getRoot();
- std::vector, std::string>> res;
+ std::vector][> res;
for (auto & attrPath : getActualAttrPaths()) {
auto attr = root->findAlongAttrPath(parseAttrPath(state, attrPath));
- if (attr) res.push_back({*attr, attrPath});
+ if (attr) res.push_back(ref(*attr));
}
return res;
}
+ref InstallableFlake::getCursor(EvalState & state)
+{
+ auto lockedFlake = getLockedFlake();
+
+ auto cache = openEvalCache(state, lockedFlake);
+ auto root = cache->getRoot();
+
+ Suggestions suggestions;
+
+ auto attrPaths = getActualAttrPaths();
+
+ for (auto & attrPath : attrPaths) {
+ debug("trying flake output attribute '%s'", attrPath);
+
+ auto attrOrSuggestions = root->findAlongAttrPath(
+ parseAttrPath(state, attrPath),
+ true
+ );
+
+ if (!attrOrSuggestions) {
+ suggestions += attrOrSuggestions.getSuggestions();
+ continue;
+ }
+
+ return *attrOrSuggestions;
+ }
+
+ throw Error(
+ suggestions,
+ "flake '%s' does not provide attribute %s",
+ flakeRef,
+ showAttrPaths(attrPaths));
+}
+
std::shared_ptr InstallableFlake::getLockedFlake() const
{
- flake::LockFlags lockFlagsApplyConfig = lockFlags;
- lockFlagsApplyConfig.applyNixConfig = true;
if (!_lockedFlake) {
+ flake::LockFlags lockFlagsApplyConfig = lockFlags;
+ lockFlagsApplyConfig.applyNixConfig = true;
_lockedFlake = std::make_shared(lockFlake(*state, flakeRef, lockFlagsApplyConfig));
}
return _lockedFlake;
@@ -687,6 +699,10 @@ std::vector> SourceExprCommand::parseInstallables(
{
std::vector> result;
+ if (readOnlyMode) {
+ settings.readOnlyMode = true;
+ }
+
if (file || expr) {
if (file && expr)
throw UsageError("'--file' and '--expr' are exclusive");
@@ -756,55 +772,20 @@ std::shared_ptr SourceExprCommand::parseInstallable(
return installables.front();
}
-BuiltPaths getBuiltPaths(ref evalStore, ref store, const DerivedPaths & hopefullyBuiltPaths)
+BuiltPaths Installable::build(
+ ref evalStore,
+ ref store,
+ Realise mode,
+ const std::vector> & installables,
+ BuildMode bMode)
{
BuiltPaths res;
- for (const auto & b : hopefullyBuiltPaths)
- std::visit(
- overloaded{
- [&](const DerivedPath::Opaque & bo) {
- res.push_back(BuiltPath::Opaque{bo.path});
- },
- [&](const DerivedPath::Built & bfd) {
- OutputPathMap outputs;
- auto drv = evalStore->readDerivation(bfd.drvPath);
- auto outputHashes = staticOutputHashes(*evalStore, drv); // FIXME: expensive
- auto drvOutputs = drv.outputsAndOptPaths(*store);
- for (auto & output : bfd.outputs) {
- if (!outputHashes.count(output))
- throw Error(
- "the derivation '%s' doesn't have an output named '%s'",
- store->printStorePath(bfd.drvPath), output);
- if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) {
- auto outputId =
- DrvOutput{outputHashes.at(output), output};
- auto realisation =
- store->queryRealisation(outputId);
- if (!realisation)
- throw Error(
- "cannot operate on an output of unbuilt "
- "content-addressed derivation '%s'",
- outputId.to_string());
- outputs.insert_or_assign(
- output, realisation->outPath);
- } else {
- // If ca-derivations isn't enabled, assume that
- // the output path is statically known.
- assert(drvOutputs.count(output));
- assert(drvOutputs.at(output).second);
- outputs.insert_or_assign(
- output, *drvOutputs.at(output).second);
- }
- }
- res.push_back(BuiltPath::Built{bfd.drvPath, outputs});
- },
- },
- b.raw());
-
+ for (auto & [_, builtPath] : build2(evalStore, store, mode, installables, bMode))
+ res.push_back(builtPath);
return res;
}
-BuiltPaths Installable::build(
+std::vector, BuiltPath>> Installable::build2(
ref evalStore,
ref store,
Realise mode,
@@ -815,39 +796,93 @@ BuiltPaths Installable::build(
settings.readOnlyMode = true;
std::vector pathsToBuild;
+ std::map>> backmap;
for (auto & i : installables) {
- auto b = i->toDerivedPaths();
- pathsToBuild.insert(pathsToBuild.end(), b.begin(), b.end());
+ for (auto b : i->toDerivedPaths()) {
+ pathsToBuild.push_back(b);
+ backmap[b].push_back(i);
+ }
}
+ std::vector, BuiltPath>> res;
+
switch (mode) {
+
case Realise::Nothing:
case Realise::Derivation:
printMissing(store, pathsToBuild, lvlError);
- return getBuiltPaths(evalStore, store, pathsToBuild);
+
+ for (auto & path : pathsToBuild) {
+ for (auto & installable : backmap[path]) {
+ std::visit(overloaded {
+ [&](const DerivedPath::Built & bfd) {
+ OutputPathMap outputs;
+ auto drv = evalStore->readDerivation(bfd.drvPath);
+ auto outputHashes = staticOutputHashes(*evalStore, drv); // FIXME: expensive
+ auto drvOutputs = drv.outputsAndOptPaths(*store);
+ for (auto & output : bfd.outputs) {
+ if (!outputHashes.count(output))
+ throw Error(
+ "the derivation '%s' doesn't have an output named '%s'",
+ store->printStorePath(bfd.drvPath), output);
+ if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) {
+ DrvOutput outputId { outputHashes.at(output), output };
+ auto realisation = store->queryRealisation(outputId);
+ if (!realisation)
+ throw Error(
+ "cannot operate on an output of the "
+ "unbuilt derivation '%s'",
+ outputId.to_string());
+ outputs.insert_or_assign(output, realisation->outPath);
+ } else {
+ // If ca-derivations isn't enabled, assume that
+ // the output path is statically known.
+ assert(drvOutputs.count(output));
+ assert(drvOutputs.at(output).second);
+ outputs.insert_or_assign(
+ output, *drvOutputs.at(output).second);
+ }
+ }
+ res.push_back({installable, BuiltPath::Built { bfd.drvPath, outputs }});
+ },
+ [&](const DerivedPath::Opaque & bo) {
+ res.push_back({installable, BuiltPath::Opaque { bo.path }});
+ },
+ }, path.raw());
+ }
+ }
+
+ break;
+
case Realise::Outputs: {
- BuiltPaths res;
for (auto & buildResult : store->buildPathsWithResults(pathsToBuild, bMode, evalStore)) {
if (!buildResult.success())
buildResult.rethrow();
- std::visit(overloaded {
- [&](const DerivedPath::Built & bfd) {
- std::map outputs;
- for (auto & path : buildResult.builtOutputs)
- outputs.emplace(path.first.outputName, path.second.outPath);
- res.push_back(BuiltPath::Built { bfd.drvPath, outputs });
- },
- [&](const DerivedPath::Opaque & bo) {
- res.push_back(BuiltPath::Opaque { bo.path });
- },
- }, buildResult.path.raw());
+
+ for (auto & installable : backmap[buildResult.path]) {
+ std::visit(overloaded {
+ [&](const DerivedPath::Built & bfd) {
+ std::map outputs;
+ for (auto & path : buildResult.builtOutputs)
+ outputs.emplace(path.first.outputName, path.second.outPath);
+ res.push_back({installable, BuiltPath::Built { bfd.drvPath, outputs }});
+ },
+ [&](const DerivedPath::Opaque & bo) {
+ res.push_back({installable, BuiltPath::Opaque { bo.path }});
+ },
+ }, buildResult.path.raw());
+ }
}
- return res;
+
+ break;
}
+
default:
assert(false);
}
+
+ return res;
}
BuiltPaths Installable::toBuiltPaths(
@@ -935,7 +970,7 @@ InstallablesCommand::InstallablesCommand()
void InstallablesCommand::prepare()
{
if (_installables.empty() && useDefaultInstallables())
- // FIXME: commands like "nix install" should not have a
+ // FIXME: commands like "nix profile install" should not have a
// default, probably.
_installables.push_back(".");
installables = parseInstallables(getStore(), _installables);
@@ -945,13 +980,14 @@ std::optional InstallablesCommand::getFlakeRefForCompletion()
{
if (_installables.empty()) {
if (useDefaultInstallables())
- return parseFlakeRef(".", absPath("."));
+ return parseFlakeRefWithFragment(".", absPath(".")).first;
return {};
}
- return parseFlakeRef(_installables.front(), absPath("."));
+ return parseFlakeRefWithFragment(_installables.front(), absPath(".")).first;
}
-InstallableCommand::InstallableCommand()
+InstallableCommand::InstallableCommand(bool supportReadOnlyMode)
+ : SourceExprCommand(supportReadOnlyMode)
{
expectArgs({
.label = "installable",
diff --git a/src/libcmd/installables.hh b/src/libcmd/installables.hh
index e172b71b0..de8b08525 100644
--- a/src/libcmd/installables.hh
+++ b/src/libcmd/installables.hh
@@ -68,7 +68,7 @@ struct Installable
UnresolvedApp toApp(EvalState & state);
- virtual std::pair toValue(EvalState & state)
+ virtual std::pair toValue(EvalState & state)
{
throw Error("argument '%s' cannot be evaluated", what());
}
@@ -80,10 +80,10 @@ struct Installable
return {};
}
- virtual std::vector, std::string>>
+ virtual std::vector][>
getCursors(EvalState & state);
- std::pair, std::string>
+ virtual ref
getCursor(EvalState & state);
virtual FlakeRef nixpkgsFlakeRef() const
@@ -98,6 +98,13 @@ struct Installable
const std::vector> & installables,
BuildMode bMode = bmNormal);
+ static std::vector, BuiltPath>> build2(
+ ref evalStore,
+ ref store,
+ Realise mode,
+ const std::vector> & installables,
+ BuildMode bMode = bmNormal);
+
static std::set toStorePaths(
ref evalStore,
ref store,
@@ -171,11 +178,17 @@ struct InstallableFlake : InstallableValue
std::vector toDerivations() override;
- std::pair toValue(EvalState & state) override;
+ std::pair toValue(EvalState & state) override;
- std::vector, std::string>>
+ /* Get a cursor to every attrpath in getActualAttrPaths() that
+ exists. */
+ std::vector][>
getCursors(EvalState & state) override;
+ /* Get a cursor to the first attrpath in getActualAttrPaths() that
+ exists, or throw an exception with suggestions if none exists. */
+ ref getCursor(EvalState & state) override;
+
std::shared_ptr getLockedFlake() const;
FlakeRef nixpkgsFlakeRef() const override;
@@ -185,9 +198,4 @@ ref openEvalCache(
EvalState & state,
std::shared_ptr lockedFlake);
-BuiltPaths getBuiltPaths(
- ref evalStore,
- ref store,
- const DerivedPaths & hopefullyBuiltPaths);
-
}
diff --git a/src/libexpr/attr-path.cc b/src/libexpr/attr-path.cc
index 7b7bc01f0..7c0705091 100644
--- a/src/libexpr/attr-path.cc
+++ b/src/libexpr/attr-path.cc
@@ -41,13 +41,13 @@ std::vector parseAttrPath(EvalState & state, std::string_view s)
}
-std::pair findAlongAttrPath(EvalState & state, const std::string & attrPath,
+std::pair findAlongAttrPath(EvalState & state, const std::string & attrPath,
Bindings & autoArgs, Value & vIn)
{
Strings tokens = parseAttrPath(attrPath);
Value * v = &vIn;
- Pos pos = noPos;
+ PosIdx pos = noPos;
for (auto & attr : tokens) {
@@ -77,13 +77,13 @@ std::pair findAlongAttrPath(EvalState & state, const std::string &
if (a == v->attrs->end()) {
std::set attrNames;
for (auto & attr : *v->attrs)
- attrNames.insert(attr.name);
+ attrNames.insert(state.symbols[attr.name]);
auto suggestions = Suggestions::bestMatches(attrNames, attr);
throw AttrPathNotFound(suggestions, "attribute '%1%' in selection path '%2%' not found", attr, attrPath);
}
v = &*a->value;
- pos = *a->pos;
+ pos = a->pos;
}
else {
@@ -106,7 +106,7 @@ std::pair findAlongAttrPath(EvalState & state, const std::string &
}
-Pos findPackageFilename(EvalState & state, Value & v, std::string what)
+std::pair findPackageFilename(EvalState & state, Value & v, std::string what)
{
Value * v2;
try {
@@ -132,9 +132,7 @@ Pos findPackageFilename(EvalState & state, Value & v, std::string what)
throw ParseError("cannot parse line number '%s'", pos);
}
- Symbol file = state.symbols.create(filename);
-
- return { foFile, file, lineno, 0 };
+ return { std::move(filename), lineno };
}
diff --git a/src/libexpr/attr-path.hh b/src/libexpr/attr-path.hh
index ff1135a06..117e0051b 100644
--- a/src/libexpr/attr-path.hh
+++ b/src/libexpr/attr-path.hh
@@ -10,14 +10,14 @@ namespace nix {
MakeError(AttrPathNotFound, Error);
MakeError(NoPositionInfo, Error);
-std::pair findAlongAttrPath(
+std::pair findAlongAttrPath(
EvalState & state,
const std::string & attrPath,
Bindings & autoArgs,
Value & vIn);
/* Heuristic to find the filename and lineno or a nix value. */
-Pos findPackageFilename(EvalState & state, Value & v, std::string what);
+std::pair findPackageFilename(EvalState & state, Value & v, std::string what);
std::vector parseAttrPath(EvalState & state, std::string_view s);
diff --git a/src/libexpr/attr-set.cc b/src/libexpr/attr-set.cc
index 52ac47e9b..877116f1f 100644
--- a/src/libexpr/attr-set.cc
+++ b/src/libexpr/attr-set.cc
@@ -26,7 +26,7 @@ Bindings * EvalState::allocBindings(size_t capacity)
/* Create a new attribute named 'name' on an existing attribute set stored
in 'vAttrs' and return the newly allocated Value which is associated with
this attribute. */
-Value * EvalState::allocAttr(Value & vAttrs, const Symbol & name)
+Value * EvalState::allocAttr(Value & vAttrs, Symbol name)
{
Value * v = allocValue();
vAttrs.attrs->push_back(Attr(name, v));
@@ -40,7 +40,7 @@ Value * EvalState::allocAttr(Value & vAttrs, std::string_view name)
}
-Value & BindingsBuilder::alloc(const Symbol & name, ptr pos)
+Value & BindingsBuilder::alloc(Symbol name, PosIdx pos)
{
auto value = state.allocValue();
bindings->push_back(Attr(name, value, pos));
@@ -48,7 +48,7 @@ Value & BindingsBuilder::alloc(const Symbol & name, ptr pos)
}
-Value & BindingsBuilder::alloc(std::string_view name, ptr pos)
+Value & BindingsBuilder::alloc(std::string_view name, PosIdx pos)
{
return alloc(state.symbols.create(name), pos);
}
diff --git a/src/libexpr/attr-set.hh b/src/libexpr/attr-set.hh
index cad9743ea..dcc73b506 100644
--- a/src/libexpr/attr-set.hh
+++ b/src/libexpr/attr-set.hh
@@ -15,18 +15,27 @@ struct Value;
/* Map one attribute name to its value. */
struct Attr
{
+ /* the placement of `name` and `pos` in this struct is important.
+ both of them are uint32 wrappers, they are next to each other
+ to make sure that Attr has no padding on 64 bit machines. that
+ way we keep Attr size at two words with no wasted space. */
Symbol name;
+ PosIdx pos;
Value * value;
- ptr pos;
- Attr(Symbol name, Value * value, ptr pos = ptr(&noPos))
- : name(name), value(value), pos(pos) { };
- Attr() : pos(&noPos) { };
+ Attr(Symbol name, Value * value, PosIdx pos = noPos)
+ : name(name), pos(pos), value(value) { };
+ Attr() { };
bool operator < (const Attr & a) const
{
return name < a.name;
}
};
+static_assert(sizeof(Attr) == 2 * sizeof(uint32_t) + sizeof(Value *),
+ "performance of the evaluator is highly sensitive to the size of Attr. "
+ "avoid introducing any padding into Attr if at all possible, and do not "
+ "introduce new fields that need not be present for almost every instance.");
+
/* Bindings contains all the attributes of an attribute set. It is defined
by its size and its capacity, the capacity being the number of Attr
elements allocated after this structure, while the size corresponds to
@@ -35,13 +44,13 @@ class Bindings
{
public:
typedef uint32_t size_t;
- ptr pos;
+ PosIdx pos;
private:
size_t size_, capacity_;
Attr attrs[0];
- Bindings(size_t capacity) : pos(&noPos), size_(0), capacity_(capacity) { }
+ Bindings(size_t capacity) : size_(0), capacity_(capacity) { }
Bindings(const Bindings & bindings) = delete;
public:
@@ -57,7 +66,7 @@ public:
attrs[size_++] = attr;
}
- iterator find(const Symbol & name)
+ iterator find(Symbol name)
{
Attr key(name, 0);
iterator i = std::lower_bound(begin(), end(), key);
@@ -65,7 +74,7 @@ public:
return end();
}
- Attr * get(const Symbol & name)
+ Attr * get(Symbol name)
{
Attr key(name, 0);
iterator i = std::lower_bound(begin(), end(), key);
@@ -73,18 +82,6 @@ public:
return nullptr;
}
- Attr & need(const Symbol & name, const Pos & pos = noPos)
- {
- auto a = get(name);
- if (!a)
- throw Error({
- .msg = hintfmt("attribute '%s' missing", name),
- .errPos = pos
- });
-
- return *a;
- }
-
iterator begin() { return &attrs[0]; }
iterator end() { return &attrs[size_]; }
@@ -98,14 +95,15 @@ public:
size_t capacity() { return capacity_; }
/* Returns the attributes in lexicographically sorted order. */
- std::vector lexicographicOrder() const
+ std::vector lexicographicOrder(const SymbolTable & symbols) const
{
std::vector res;
res.reserve(size_);
for (size_t n = 0; n < size_; n++)
res.emplace_back(&attrs[n]);
- std::sort(res.begin(), res.end(), [](const Attr * a, const Attr * b) {
- return (const std::string &) a->name < (const std::string &) b->name;
+ std::sort(res.begin(), res.end(), [&](const Attr * a, const Attr * b) {
+ std::string_view sa = symbols[a->name], sb = symbols[b->name];
+ return sa < sb;
});
return res;
}
@@ -130,7 +128,7 @@ public:
: bindings(bindings), state(state)
{ }
- void insert(Symbol name, Value * value, ptr pos = ptr(&noPos))
+ void insert(Symbol name, Value * value, PosIdx pos = noPos)
{
insert(Attr(name, value, pos));
}
@@ -145,9 +143,9 @@ public:
bindings->push_back(attr);
}
- Value & alloc(const Symbol & name, ptr pos = ptr(&noPos));
+ Value & alloc(Symbol name, PosIdx pos = noPos);
- Value & alloc(std::string_view name, ptr pos = ptr(&noPos));
+ Value & alloc(std::string_view name, PosIdx pos = noPos);
Bindings * finish()
{
diff --git a/src/libexpr/eval-cache.cc b/src/libexpr/eval-cache.cc
index fd6dd0e40..dc52b9fa0 100644
--- a/src/libexpr/eval-cache.cc
+++ b/src/libexpr/eval-cache.cc
@@ -21,6 +21,8 @@ struct AttrDb
{
std::atomic_bool failed{false};
+ const Store & cfg;
+
struct State
{
SQLite db;
@@ -33,8 +35,15 @@ struct AttrDb
std::unique_ptr> _state;
- AttrDb(const Hash & fingerprint)
- : _state(std::make_unique>())
+ SymbolTable & symbols;
+
+ AttrDb(
+ const Store & cfg,
+ const Hash & fingerprint,
+ SymbolTable & symbols)
+ : cfg(cfg)
+ , _state(std::make_unique>())
+ , symbols(symbols)
{
auto state(_state->lock());
@@ -97,7 +106,7 @@ struct AttrDb
state->insertAttribute.use()
(key.first)
- (key.second)
+ (symbols[key.second])
(AttrType::FullAttrs)
(0, false).exec();
@@ -107,7 +116,7 @@ struct AttrDb
for (auto & attr : attrs)
state->insertAttribute.use()
(rowId)
- (attr)
+ (symbols[attr])
(AttrType::Placeholder)
(0, false).exec();
@@ -132,14 +141,14 @@ struct AttrDb
}
state->insertAttributeWithContext.use()
(key.first)
- (key.second)
+ (symbols[key.second])
(AttrType::String)
(s)
(ctx).exec();
} else {
state->insertAttribute.use()
(key.first)
- (key.second)
+ (symbols[key.second])
(AttrType::String)
(s).exec();
}
@@ -158,7 +167,7 @@ struct AttrDb
state->insertAttribute.use()
(key.first)
- (key.second)
+ (symbols[key.second])
(AttrType::Bool)
(b ? 1 : 0).exec();
@@ -174,7 +183,7 @@ struct AttrDb
state->insertAttribute.use()
(key.first)
- (key.second)
+ (symbols[key.second])
(AttrType::Placeholder)
(0, false).exec();
@@ -190,7 +199,7 @@ struct AttrDb
state->insertAttribute.use()
(key.first)
- (key.second)
+ (symbols[key.second])
(AttrType::Missing)
(0, false).exec();
@@ -206,7 +215,7 @@ struct AttrDb
state->insertAttribute.use()
(key.first)
- (key.second)
+ (symbols[key.second])
(AttrType::Misc)
(0, false).exec();
@@ -222,7 +231,7 @@ struct AttrDb
state->insertAttribute.use()
(key.first)
- (key.second)
+ (symbols[key.second])
(AttrType::Failed)
(0, false).exec();
@@ -230,13 +239,11 @@ struct AttrDb
});
}
- std::optional> getAttr(
- AttrKey key,
- SymbolTable & symbols)
+ std::optional> getAttr(AttrKey key)
{
auto state(_state->lock());
- auto queryAttribute(state->queryAttribute.use()(key.first)(key.second));
+ auto queryAttribute(state->queryAttribute.use()(key.first)(symbols[key.second]));
if (!queryAttribute.next()) return {};
auto rowId = (AttrType) queryAttribute.getInt(0);
@@ -250,14 +257,14 @@ struct AttrDb
std::vector attrs;
auto queryAttributes(state->queryAttributes.use()(rowId));
while (queryAttributes.next())
- attrs.push_back(symbols.create(queryAttributes.getStr(0)));
+ attrs.emplace_back(symbols.create(queryAttributes.getStr(0)));
return {{rowId, attrs}};
}
case AttrType::String: {
- std::vector> context;
+ NixStringContext context;
if (!queryAttribute.isNull(3))
for (auto & s : tokenizeString>(queryAttribute.getStr(3), ";"))
- context.push_back(decodeContext(s));
+ context.push_back(decodeContext(cfg, s));
return {{rowId, string_t{queryAttribute.getStr(2), context}}};
}
case AttrType::Bool:
@@ -274,10 +281,13 @@ struct AttrDb
}
};
-static std::shared_ptr makeAttrDb(const Hash & fingerprint)
+static std::shared_ptr makeAttrDb(
+ const Store & cfg,
+ const Hash & fingerprint,
+ SymbolTable & symbols)
{
try {
- return std::make_shared(fingerprint);
+ return std::make_shared(cfg, fingerprint, symbols);
} catch (SQLiteError &) {
ignoreException();
return nullptr;
@@ -288,7 +298,7 @@ EvalCache::EvalCache(
std::optional> useCache,
EvalState & state,
RootLoader rootLoader)
- : db(useCache ? makeAttrDb(*useCache) : nullptr)
+ : db(useCache ? makeAttrDb(*state.store, *useCache, state.symbols) : nullptr)
, state(state)
, rootLoader(rootLoader)
{
@@ -303,9 +313,9 @@ Value * EvalCache::getRootValue()
return *value;
}
-std::shared_ptr EvalCache::getRoot()
+ref EvalCache::getRoot()
{
- return std::make_shared(ref(shared_from_this()), std::nullopt);
+ return make_ref(ref(shared_from_this()), std::nullopt);
}
AttrCursor::AttrCursor(
@@ -324,8 +334,7 @@ AttrKey AttrCursor::getKey()
if (!parent)
return {0, root->state.sEpsilon};
if (!parent->first->cachedValue) {
- parent->first->cachedValue = root->db->getAttr(
- parent->first->getKey(), root->state.symbols);
+ parent->first->cachedValue = root->db->getAttr(parent->first->getKey());
assert(parent->first->cachedValue);
}
return {parent->first->cachedValue->first, parent->second};
@@ -366,12 +375,12 @@ std::vector AttrCursor::getAttrPath(Symbol name) const
std::string AttrCursor::getAttrPathStr() const
{
- return concatStringsSep(".", getAttrPath());
+ return concatStringsSep(".", root->state.symbols.resolve(getAttrPath()));
}
std::string AttrCursor::getAttrPathStr(Symbol name) const
{
- return concatStringsSep(".", getAttrPath(name));
+ return concatStringsSep(".", root->state.symbols.resolve(getAttrPath(name)));
}
Value & AttrCursor::forceValue()
@@ -411,25 +420,25 @@ Suggestions AttrCursor::getSuggestionsForAttr(Symbol name)
auto attrNames = getAttrs();
std::set strAttrNames;
for (auto & name : attrNames)
- strAttrNames.insert(std::string(name));
+ strAttrNames.insert(root->state.symbols[name]);
- return Suggestions::bestMatches(strAttrNames, name);
+ return Suggestions::bestMatches(strAttrNames, root->state.symbols[name]);
}
std::shared_ptr AttrCursor::maybeGetAttr(Symbol name, bool forceErrors)
{
if (root->db) {
if (!cachedValue)
- cachedValue = root->db->getAttr(getKey(), root->state.symbols);
+ cachedValue = root->db->getAttr(getKey());
if (cachedValue) {
if (auto attrs = std::get_if>(&cachedValue->second)) {
for (auto & attr : *attrs)
if (attr == name)
- return std::make_shared(root, std::make_pair(shared_from_this(), name));
+ return std::make_shared(root, std::make_pair(shared_from_this(), attr));
return nullptr;
} else if (std::get_if(&cachedValue->second)) {
- auto attr = root->db->getAttr({cachedValue->first, name}, root->state.symbols);
+ auto attr = root->db->getAttr({cachedValue->first, name});
if (attr) {
if (std::get_if(&attr->second))
return nullptr;
@@ -519,7 +528,7 @@ std::string AttrCursor::getString()
{
if (root->db) {
if (!cachedValue)
- cachedValue = root->db->getAttr(getKey(), root->state.symbols);
+ cachedValue = root->db->getAttr(getKey());
if (cachedValue && !std::get_if(&cachedValue->second)) {
if (auto s = std::get_if(&cachedValue->second)) {
debug("using cached string attribute '%s'", getAttrPathStr());
@@ -541,12 +550,12 @@ string_t AttrCursor::getStringWithContext()
{
if (root->db) {
if (!cachedValue)
- cachedValue = root->db->getAttr(getKey(), root->state.symbols);
+ cachedValue = root->db->getAttr(getKey());
if (cachedValue && !std::get_if(&cachedValue->second)) {
if (auto s = std::get_if(&cachedValue->second)) {
bool valid = true;
for (auto & c : s->second) {
- if (!root->state.store->isValidPath(root->state.store->parseStorePath(c.first))) {
+ if (!root->state.store->isValidPath(c.first)) {
valid = false;
break;
}
@@ -563,7 +572,7 @@ string_t AttrCursor::getStringWithContext()
auto & v = forceValue();
if (v.type() == nString)
- return {v.string.s, v.getContext()};
+ return {v.string.s, v.getContext(*root->state.store)};
else if (v.type() == nPath)
return {v.path, {}};
else
@@ -574,7 +583,7 @@ bool AttrCursor::getBool()
{
if (root->db) {
if (!cachedValue)
- cachedValue = root->db->getAttr(getKey(), root->state.symbols);
+ cachedValue = root->db->getAttr(getKey());
if (cachedValue && !std::get_if(&cachedValue->second)) {
if (auto b = std::get_if(&cachedValue->second)) {
debug("using cached Boolean attribute '%s'", getAttrPathStr());
@@ -596,7 +605,7 @@ std::vector AttrCursor::getAttrs()
{
if (root->db) {
if (!cachedValue)
- cachedValue = root->db->getAttr(getKey(), root->state.symbols);
+ cachedValue = root->db->getAttr(getKey());
if (cachedValue && !std::get_if(&cachedValue->second)) {
if (auto attrs = std::get_if>(&cachedValue->second)) {
debug("using cached attrset attribute '%s'", getAttrPathStr());
@@ -614,8 +623,9 @@ std::vector AttrCursor::getAttrs()
std::vector attrs;
for (auto & attr : *getValue().attrs)
attrs.push_back(attr.name);
- std::sort(attrs.begin(), attrs.end(), [](const Symbol & a, const Symbol & b) {
- return (const std::string &) a < (const std::string &) b;
+ std::sort(attrs.begin(), attrs.end(), [&](Symbol a, Symbol b) {
+ std::string_view sa = root->state.symbols[a], sb = root->state.symbols[b];
+ return sa < sb;
});
if (root->db)
diff --git a/src/libexpr/eval-cache.hh b/src/libexpr/eval-cache.hh
index 40f1d4ffc..b0709ebc2 100644
--- a/src/libexpr/eval-cache.hh
+++ b/src/libexpr/eval-cache.hh
@@ -33,7 +33,7 @@ public:
EvalState & state,
RootLoader rootLoader);
- std::shared_ptr getRoot();
+ ref getRoot();
};
enum AttrType {
@@ -52,7 +52,7 @@ struct misc_t {};
struct failed_t {};
typedef uint64_t AttrId;
typedef std::pair AttrKey;
-typedef std::pair>> string_t;
+typedef std::pair string_t;
typedef std::variant<
std::vector,
@@ -104,6 +104,8 @@ public:
ref getAttr(std::string_view name);
+ /* Get an attribute along a chain of attrsets. Note that this does
+ not auto-call functors or functions. */
OrSuggestions][> findAlongAttrPath(const std::vector & attrPath, bool force = false);
std::string getString();
diff --git a/src/libexpr/eval-inline.hh b/src/libexpr/eval-inline.hh
index 025459c00..30d0f1e9c 100644
--- a/src/libexpr/eval-inline.hh
+++ b/src/libexpr/eval-inline.hh
@@ -2,27 +2,8 @@
#include "eval.hh"
-#define LocalNoInline(f) static f __attribute__((noinline)); f
-#define LocalNoInlineNoReturn(f) static f __attribute__((noinline, noreturn)); f
-
namespace nix {
-LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s))
-{
- throw EvalError({
- .msg = hintfmt(s),
- .errPos = pos
- });
-}
-
-LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const Value & v))
-{
- throw TypeError({
- .msg = hintfmt(s, showType(v)),
- .errPos = pos
- });
-}
-
/* Note: Various places expect the allocated memory to be zeroed. */
[[gnu::always_inline]]
@@ -99,7 +80,7 @@ Env & EvalState::allocEnv(size_t size)
[[gnu::always_inline]]
-void EvalState::forceValue(Value & v, const Pos & pos)
+void EvalState::forceValue(Value & v, const PosIdx pos)
{
forceValue(v, [&]() { return pos; });
}
@@ -128,7 +109,7 @@ void EvalState::forceValue(Value & v, Callable getPos)
[[gnu::always_inline]]
-inline void EvalState::forceAttrs(Value & v, const Pos & pos, std::string_view errorCtx)
+inline void EvalState::forceAttrs(Value & v, const PosIdx pos, std::string_view errorCtx)
{
forceAttrs(v, [&]() { return pos; }, errorCtx);
}
@@ -144,15 +125,15 @@ inline void EvalState::forceAttrs(Value & v, Callable getPos, std::string_view e
throwTypeError(noPos, "value is %1% while a set was expected", v);
}
} catch (Error & e) {
- Pos pos = getPos();
- e.addTrace(pos, errorCtx);
+ PosIdx pos = getPos();
+ e.addTrace(positions[pos], errorCtx);
throw;
}
}
[[gnu::always_inline]]
-inline void EvalState::forceList(Value & v, const Pos & pos, std::string_view errorCtx)
+inline void EvalState::forceList(Value & v, const PosIdx pos, std::string_view errorCtx)
{
try {
forceValue(v, noPos);
@@ -160,7 +141,7 @@ inline void EvalState::forceList(Value & v, const Pos & pos, std::string_view er
throwTypeError(noPos, "value is %1% while a list was expected", v);
}
} catch (Error & e) {
- e.addTrace(pos, errorCtx);
+ e.addTrace(positions[pos], errorCtx);
throw;
}
}
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index 6d9129bd3..5523ba7bb 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -96,20 +96,21 @@ RootValue allocRootValue(Value * v)
}
-void printValue(std::ostream & str, std::set & seen, const Value & v)
+void Value::print(const SymbolTable & symbols, std::ostream & str,
+ std::set * seen) const
{
checkInterrupt();
- switch (v.internalType) {
+ switch (internalType) {
case tInt:
- str << v.integer;
+ str << integer;
break;
case tBool:
- str << (v.boolean ? "true" : "false");
+ str << (boolean ? "true" : "false");
break;
case tString:
str << "\"";
- for (const char * i = v.string.s; *i; i++)
+ for (const char * i = string.s; *i; i++)
if (*i == '\"' || *i == '\\') str << "\\" << *i;
else if (*i == '\n') str << "\\n";
else if (*i == '\r') str << "\\r";
@@ -119,19 +120,19 @@ void printValue(std::ostream & str, std::set & seen, const Value &
str << "\"";
break;
case tPath:
- str << v.path; // !!! escaping?
+ str << path; // !!! escaping?
break;
case tNull:
str << "null";
break;
case tAttrs: {
- if (!v.attrs->empty() && !seen.insert(v.attrs).second)
- str << "";
+ if (seen && !attrs->empty() && !seen->insert(attrs).second)
+ str << "«repeated»";
else {
str << "{ ";
- for (auto & i : v.attrs->lexicographicOrder()) {
- str << i->name << " = ";
- printValue(str, seen, *i->value);
+ for (auto & i : attrs->lexicographicOrder(symbols)) {
+ str << symbols[i->name] << " = ";
+ i->value->print(symbols, str, seen);
str << "; ";
}
str << "}";
@@ -141,12 +142,12 @@ void printValue(std::ostream & str, std::set & seen, const Value &
case tList1:
case tList2:
case tListN:
- if (v.listSize() && !seen.insert(v.listElems()).second)
- str << "";
+ if (seen && listSize() && !seen->insert(listElems()).second)
+ str << "«repeated»";
else {
str << "[ ";
- for (auto v2 : v.listItems()) {
- printValue(str, seen, *v2);
+ for (auto v2 : listItems()) {
+ v2->print(symbols, str, seen);
str << " ";
}
str << "]";
@@ -166,10 +167,10 @@ void printValue(std::ostream & str, std::set & seen, const Value &
str << "";
break;
case tExternal:
- str << *v.external;
+ str << *external;
break;
case tFloat:
- str << v.fpoint;
+ str << fpoint;
break;
default:
abort();
@@ -177,11 +178,18 @@ void printValue(std::ostream & str, std::set & seen, const Value &
}
-std::ostream & operator << (std::ostream & str, const Value & v)
+void Value::print(const SymbolTable & symbols, std::ostream & str, bool showRepeated) const
{
std::set seen;
- printValue(str, seen, v);
- return str;
+ print(symbols, str, showRepeated ? nullptr : &seen);
+}
+
+
+std::string printValue(const EvalState & state, const Value & v)
+{
+ std::ostringstream out;
+ v.print(state.symbols, out);
+ return out.str();
}
@@ -230,10 +238,10 @@ std::string showType(const Value & v)
}
}
-Pos Value::determinePos(const Pos & pos) const
+PosIdx Value::determinePos(const PosIdx pos) const
{
switch (internalType) {
- case tAttrs: return *attrs->pos;
+ case tAttrs: return attrs->pos;
case tLambda: return lambda.fun->pos;
case tApp: return app.left->determinePos(pos);
default: return pos;
@@ -302,7 +310,7 @@ static BoehmGCStackAllocator boehmGCStackAllocator;
static Symbol getName(const AttrName & name, EvalState & state, Env & env)
{
- if (name.symbol.set()) {
+ if (name.symbol) {
return name.symbol;
} else {
Value nameValue;
@@ -430,6 +438,7 @@ EvalState::EvalState(
, sBuilder(symbols.create("builder"))
, sArgs(symbols.create("args"))
, sContentAddressed(symbols.create("__contentAddressed"))
+ , sImpure(symbols.create("__impure"))
, sOutputHash(symbols.create("outputHash"))
, sOutputHashAlgo(symbols.create("outputHashAlgo"))
, sOutputHashMode(symbols.create("outputHashMode"))
@@ -501,23 +510,6 @@ EvalState::~EvalState()
}
-void EvalState::requireExperimentalFeatureOnEvaluation(
- const ExperimentalFeature & feature,
- const std::string_view fName,
- const Pos & pos)
-{
- if (!settings.isExperimentalFeatureEnabled(feature)) {
- throw EvalError({
- .msg = hintfmt(
- "cannot call '%2%' because experimental Nix feature '%1%' is disabled. You can enable it via '--extra-experimental-features %1%'.",
- feature,
- fName
- ),
- .errPos = pos
- });
- }
-}
-
void EvalState::allowPath(const Path & path)
{
if (allowedPaths)
@@ -648,7 +640,7 @@ void EvalState::addConstant(const std::string & name, Value * v)
Value * EvalState::addPrimOp(const std::string & name,
size_t arity, PrimOpFun primOp)
{
- return addPrimOp(PrimOp { .fun = primOp, .arity = arity, .name = symbols.create(name) });
+ return addPrimOp(PrimOp { .fun = primOp, .arity = arity, .name = name });
}
@@ -659,21 +651,21 @@ Value * EvalState::addPrimOp(PrimOp && primOp)
if (primOp.arity == 0) {
primOp.arity = 1;
auto vPrimOp = allocValue();
- vPrimOp->mkPrimOp(new PrimOp(std::move(primOp)));
+ vPrimOp->mkPrimOp(new PrimOp(primOp));
Value v;
v.mkApp(vPrimOp, vPrimOp);
return addConstant(primOp.name, v);
}
- Symbol envName = primOp.name;
+ auto envName = symbols.create(primOp.name);
if (hasPrefix(primOp.name, "__"))
- primOp.name = symbols.create(std::string(primOp.name, 2));
+ primOp.name = primOp.name.substr(2);
Value * v = allocValue();
- v->mkPrimOp(new PrimOp(std::move(primOp)));
+ v->mkPrimOp(new PrimOp(primOp));
staticBaseEnv.vars.emplace_back(envName, baseEnvDispl);
baseEnv.values[baseEnvDispl++] = v;
- baseEnv.values[0]->attrs->push_back(Attr(primOp.name, v));
+ baseEnv.values[0]->attrs->push_back(Attr(symbols.create(primOp.name), v));
return v;
}
@@ -690,7 +682,7 @@ std::optional EvalState::getDoc(Value & v)
auto v2 = &v;
if (v2->primOp->doc)
return Doc {
- .pos = noPos,
+ .pos = {},
.name = v2->primOp->name,
.arity = v2->primOp->arity,
.args = v2->primOp->args,
@@ -706,140 +698,164 @@ std::optional EvalState::getDoc(Value & v)
evaluator. So here are some helper functions for throwing
exceptions. */
-LocalNoInlineNoReturn(void throwTypeErrorWithTrace(
- const Pos & pos,
+void EvalState::throwTypeErrorWithTrace(
+ const PosIdx pos,
const char * s,
- const std::string_view & s2,
+ const std::string_view s2,
const Symbol & sym,
- const Pos & p2,
- const std::string_view & s3))
+ const PosIdx p2,
+ const std::string_view s3) const
{
auto e = TypeError(ErrorInfo {
- .msg = hintfmt(s, s2, sym),
- .errPos = pos,
+ .msg = hintfmt(s, s2, symbols[sym]),
+ .errPos = positions[pos],
});
- e.addTrace(p2, s3);
+ e.addTrace(positions[p2], s3);
throw e;
}
-LocalNoInlineNoReturn(void throwTypeErrorWithTrace(
- const Pos & pos,
+void EvalState::throwTypeErrorWithTrace(
+ const PosIdx pos,
const Suggestions & suggestions,
const char * s,
- const std::string_view & s2,
+ const std::string_view s2,
const Symbol & sym,
- const Pos & p2,
- const std::string_view & s3))
+ const PosIdx p2,
+ const std::string_view s3) const
{
auto e = TypeError(ErrorInfo {
- .msg = hintfmt(s, s2, sym),
- .errPos = pos,
+ .msg = hintfmt(s, s2, symbols[sym]),
+ .errPos = positions[pos],
.suggestions = suggestions
});
- e.addTrace(p2, s3);
+ e.addTrace(positions[p2], s3);
throw e;
}
-LocalNoInlineNoReturn(void throwTypeErrorWithTrace(const char * s, const std::string & s2, const Pos & p2, const std::string_view s3))
+void EvalState::throwTypeErrorWithTrace(const char * s, const std::string_view s2, const PosIdx p2, const std::string_view s3) const
{
auto e = TypeError(ErrorInfo {
.msg = hintfmt(s, s2),
});
- e.addTrace(p2, s3);
+ e.addTrace(positions[p2], s3);
throw e;
}
-LocalNoInlineNoReturn(void throwEvalErrorWithTrace(const char * s, const std::string & s2, const Pos & p2, const std::string_view s3))
+void EvalState::throwEvalErrorWithTrace(const char * s, const std::string_view s2, const PosIdx p2, const std::string_view s3) const
{
auto e = EvalError(ErrorInfo {
.msg = hintfmt(s, s2),
});
- e.addTrace(p2, s3);
+ e.addTrace(positions[p2], s3);
throw e;
}
-LocalNoInlineNoReturn(void throwEvalErrorWithTrace(const char * s, const std::string_view & s2, const std::string_view & s3, const Pos & p2, const std::string_view s4))
+void EvalState::throwEvalErrorWithTrace(const char * s, const std::string_view s2, const std::string_view s3, const PosIdx p2, const std::string_view s4) const
{
auto e = EvalError(ErrorInfo {
.msg = hintfmt(s, s2, s3),
});
- e.addTrace(p2, s4);
+ e.addTrace(positions[p2], s4);
throw e;
}
-LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const Suggestions & suggestions, const char * s, const std::string & s2))
+void EvalState::throwEvalError(const PosIdx pos, const Suggestions & suggestions, const char * s, const std::string_view s2) const
{
throw EvalError(ErrorInfo {
.msg = hintfmt(s, s2),
- .errPos = pos,
+ .errPos = positions[pos],
.suggestions = suggestions,
});
}
-LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const Value & v))
+void EvalState::throwEvalError(const PosIdx pos, const char * s) const
+{
+ throw EvalError(ErrorInfo {
+ .msg = hintfmt(s),
+ .errPos = positions[pos]
+ });
+}
+
+void EvalState::throwEvalError(const PosIdx pos, const char * s, const Value & v) const
{
throw EvalError(ErrorInfo {
.msg = hintfmt(s, showType(v)),
- .errPos = pos
+ .errPos = positions[pos]
});
}
-LocalNoInlineNoReturn(void throwEvalError(const Pos & p1, const char * s, const Symbol & sym, const Pos & p2))
+void EvalState::throwEvalError(const PosIdx pos, const char * s, const std::string_view s2) const
+{
+ throw EvalError(ErrorInfo {
+ .msg = hintfmt(s, s2),
+ .errPos = positions[pos]
+ });
+}
+
+void EvalState::throwEvalError(const PosIdx p1, const char * s, const Symbol sym, const PosIdx p2) const
{
// p1 is where the error occurred; p2 is a position mentioned in the message.
throw EvalError(ErrorInfo {
- .msg = hintfmt(s, sym, p2),
- .errPos = p1
+ .msg = hintfmt(s, symbols[sym], positions[p2]),
+ .errPos = positions[p1]
});
}
-LocalNoInlineNoReturn(void throwEvalError(const char * s, const std::string_view & s1))
+void EvalState::throwEvalError(const char * s, const std::string_view s1) const
{
throw EvalError(s, s1);
}
-LocalNoInlineNoReturn(void throwEvalError(const char * s, const std::string_view & s1, const std::string_view & s2))
+void EvalState::throwEvalError(const char * s, const std::string_view s1, const std::string_view s2) const
{
throw EvalError(s, s1, s2);
}
-LocalNoInlineNoReturn(void throwTypeError(const char * s, const Value & v))
+void EvalState::throwTypeError(const char * s, const Value & v) const
{
throw TypeError(s, showType(v));
}
-LocalNoInlineNoReturn(void throwAssertionError(const Pos & pos, const char * s, const std::string & s1))
+void EvalState::throwTypeError(const PosIdx pos, const char * s, const Value & v) const
+{
+ throw TypeError(ErrorInfo {
+ .msg = hintfmt(s, showType(v)),
+ .errPos = positions[pos]
+ });
+}
+
+void EvalState::throwAssertionError(const PosIdx pos, const char * s, const std::string & s1) const
{
throw AssertionError({
.msg = hintfmt(s, s1),
- .errPos = pos
+ .errPos = positions[pos]
});
}
-LocalNoInlineNoReturn(void throwUndefinedVarError(const Pos & pos, const char * s, const std::string & s1))
+void EvalState::throwUndefinedVarError(const PosIdx pos, const char * s, const std::string & s1) const
{
throw UndefinedVarError({
.msg = hintfmt(s, s1),
- .errPos = pos
+ .errPos = positions[pos]
});
}
-LocalNoInlineNoReturn(void throwMissingArgumentError(const Pos & pos, const char * s, const std::string & s1))
+void EvalState::throwMissingArgumentError(const PosIdx pos, const char * s, const std::string & s1) const
{
throw MissingArgumentError({
.msg = hintfmt(s, s1),
- .errPos = pos
+ .errPos = positions[pos]
});
}
-LocalNoInline(void addErrorTrace(Error & e, const char * s, const std::string & s2))
+void EvalState::addErrorTrace(Error & e, const char * s, const std::string & s2) const
{
e.addTrace(std::nullopt, s, s2);
}
-LocalNoInline(void addErrorTrace(Error & e, const Pos & pos, const char * s, const std::string & s2))
+void EvalState::addErrorTrace(Error & e, const PosIdx pos, const char * s, const std::string & s2) const
{
- e.addTrace(pos, s, s2);
+ e.addTrace(positions[pos], s, s2);
}
@@ -896,11 +912,11 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval)
}
Bindings::iterator j = env->values[0]->attrs->find(var.name);
if (j != env->values[0]->attrs->end()) {
- if (countCalls) attrSelects[*j->pos]++;
+ if (countCalls) attrSelects[j->pos]++;
return j->value;
}
if (!env->prevWith)
- throwUndefinedVarError(var.pos, "undefined variable '%1%'", var.name);
+ throwUndefinedVarError(var.pos, "undefined variable '%1%'", symbols[var.name]);
for (size_t l = env->prevWith; l; --l, env = env->up) ;
}
}
@@ -930,13 +946,14 @@ void EvalState::mkThunk_(Value & v, Expr * expr)
}
-void EvalState::mkPos(Value & v, ptr pos)
+void EvalState::mkPos(Value & v, PosIdx p)
{
- if (pos->file.set()) {
+ auto pos = positions[p];
+ if (!pos.file.empty()) {
auto attrs = buildBindings(3);
- attrs.alloc(sFile).mkString(pos->file);
- attrs.alloc(sLine).mkInt(pos->line);
- attrs.alloc(sColumn).mkInt(pos->column);
+ attrs.alloc(sFile).mkString(pos.file);
+ attrs.alloc(sLine).mkInt(pos.line);
+ attrs.alloc(sColumn).mkInt(pos.column);
v.mkAttrs(attrs);
} else
v.mkNull();
@@ -1057,7 +1074,7 @@ void EvalState::eval(Expr * e, Value & v)
}
-inline bool EvalState::evalBool(Env & env, Expr * e, const Pos & pos, std::string_view errorCtx)
+inline bool EvalState::evalBool(Env & env, Expr * e, const PosIdx pos, std::string_view errorCtx)
{
try {
Value v;
@@ -1066,20 +1083,20 @@ inline bool EvalState::evalBool(Env & env, Expr * e, const Pos & pos, std::strin
throwTypeError("value is %1% while a Boolean was expected", v);
return v.boolean;
} catch (Error & e) {
- e.addTrace(pos, errorCtx);
+ e.addTrace(positions[pos], errorCtx);
throw;
}
}
-inline void EvalState::evalAttrs(Env & env, Expr * e, Value & v, const Pos & pos, std::string_view errorCtx)
+inline void EvalState::evalAttrs(Env & env, Expr * e, Value & v, const PosIdx pos, std::string_view errorCtx)
{
try {
e->eval(*this, env, v);
if (v.type() != nAttrs)
throwTypeError("value is %1% while a set was expected", v);
} catch (Error & e) {
- e.addTrace(pos, errorCtx);
+ e.addTrace(positions[pos], errorCtx);
throw;
}
}
@@ -1141,7 +1158,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
} else
vAttr = i.second.e->maybeThunk(state, i.second.inherited ? env : env2);
env2.values[displ++] = vAttr;
- v.attrs->push_back(Attr(i.first, vAttr, ptr(&i.second.pos)));
+ v.attrs->push_back(Attr(i.first, vAttr, i.second.pos));
}
/* If the rec contains an attribute called `__overrides', then
@@ -1173,7 +1190,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
else
for (auto & i : attrs)
- v.attrs->push_back(Attr(i.first, i.second.e->maybeThunk(state, env), ptr(&i.second.pos)));
+ v.attrs->push_back(Attr(i.first, i.second.e->maybeThunk(state, env), i.second.pos));
/* Dynamic attrs apply *after* rec and __overrides. */
for (auto & i : dynamicAttrs) {
@@ -1183,18 +1200,18 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
if (nameVal.type() == nNull)
continue;
state.forceStringNoCtx(nameVal, i.pos, "while evaluating the name of a dynamic attribute");
- Symbol nameSym = state.symbols.create(nameVal.string.s);
+ auto nameSym = state.symbols.create(nameVal.string.s);
Bindings::iterator j = v.attrs->find(nameSym);
if (j != v.attrs->end())
- throwEvalError(i.pos, "dynamic attribute '%1%' already defined at %2%", nameSym, *j->pos);
+ state.throwEvalError(i.pos, "dynamic attribute '%1%' already defined at %2%", nameSym, j->pos);
i.valueExpr->setName(nameSym);
/* Keep sorted order so find can catch duplicates */
- v.attrs->push_back(Attr(nameSym, i.valueExpr->maybeThunk(state, *dynamicEnv), ptr(&i.pos)));
+ v.attrs->push_back(Attr(nameSym, i.valueExpr->maybeThunk(state, *dynamicEnv), i.pos));
v.attrs->sort(); // FIXME: inefficient
}
- v.attrs->pos = ptr(&pos);
+ v.attrs->pos = pos;
}
@@ -1239,10 +1256,12 @@ static std::string showAttrPath(EvalState & state, Env & env, const AttrPath & a
for (auto & i : attrPath) {
if (!first) out << '.'; else first = false;
try {
- out << getName(i, state, env);
+ out << state.symbols[getName(i, state, env)];
} catch (Error & e) {
- assert(!i.symbol.set());
- out << "\"${" << *i.expr << "}\"";
+ assert(!i.symbol);
+ out << "\"${";
+ i.expr->show(state.symbols, out);
+ out << "}\"";
}
}
return out.str();
@@ -1252,7 +1271,7 @@ static std::string showAttrPath(EvalState & state, Env & env, const AttrPath & a
void ExprSelect::eval(EvalState & state, Env & env, Value & v)
{
Value vTmp;
- ptr pos2(&noPos);
+ PosIdx pos2;
Value * vAttrs = &vTmp;
e->eval(state, env, vTmp);
@@ -1262,7 +1281,7 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
for (auto & i : attrPath) {
state.nrLookups++;
Bindings::iterator j;
- Symbol name = getName(i, state, env);
+ auto name = getName(i, state, env);
if (def) {
state.forceValue(*vAttrs, pos);
if (vAttrs->type() != nAttrs ||
@@ -1276,23 +1295,24 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
if ((j = vAttrs->attrs->find(name)) == vAttrs->attrs->end()) {
std::set allAttrNames;
for (auto & attr : *vAttrs->attrs)
- allAttrNames.insert(attr.name);
- throwEvalError(
+ allAttrNames.insert(state.symbols[attr.name]);
+ state.throwEvalError(
pos,
- Suggestions::bestMatches(allAttrNames, name),
- "attribute '%1%' missing", name);
+ Suggestions::bestMatches(allAttrNames, state.symbols[name]),
+ "attribute '%1%' missing", state.symbols[name]);
}
}
vAttrs = j->value;
pos2 = j->pos;
- if (state.countCalls) state.attrSelects[*pos2]++;
+ if (state.countCalls) state.attrSelects[pos2]++;
}
- state.forceValue(*vAttrs, (*pos2 != noPos ? *pos2 : this->pos ) );
+ state.forceValue(*vAttrs, (pos2 ? pos2 : this->pos ) );
} catch (Error & e) {
- if (*pos2 != noPos && pos2->file != state.sDerivationNix)
- addErrorTrace(e, *pos2, "while evaluating the attribute '%1%'",
+ auto pos2r = state.positions[pos2];
+ if (pos2 && pos2r.file != state.derivationNixPath)
+ state.addErrorTrace(e, pos2, "while evaluating the attribute '%1%'",
showAttrPath(state, env, attrPath));
throw;
}
@@ -1311,7 +1331,7 @@ void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v)
for (auto & i : attrPath) {
state.forceValue(*vAttrs, noPos);
Bindings::iterator j;
- Symbol name = getName(i, state, env);
+ auto name = getName(i, state, env);
if (vAttrs->type() != nAttrs ||
(j = vAttrs->attrs->find(name)) == vAttrs->attrs->end())
{
@@ -1331,14 +1351,11 @@ void ExprLambda::eval(EvalState & state, Env & env, Value & v)
v.mkLambda(&env, this);
}
-const std::string prettyLambdaName(const ExprLambda & e)
+void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & vRes, const PosIdx pos)
{
- return e.name.set() ? std::string(e.name): "anonymous lambda";
-}
-
-void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & vRes, const Pos & pos)
-{
- auto trace = evalSettings.traceFunctionCalls ? std::make_unique(pos) : nullptr;
+ auto trace = evalSettings.traceFunctionCalls
+ ? std::make_unique(positions[pos])
+ : nullptr;
forceValue(fun, pos);
@@ -1363,7 +1380,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
ExprLambda & lambda(*vCur.lambda.fun);
auto size =
- (lambda.arg.empty() ? 0 : 1) +
+ (!lambda.arg ? 0 : 1) +
(lambda.hasFormals() ? lambda.formals->formals.size() : 0);
Env & env2(allocEnv(size));
env2.up = vCur.lambda.env;
@@ -1377,11 +1394,11 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
try {
forceAttrs(*args[0], lambda.pos, "while evaluating the value passed for the lambda argument");
} catch (Error & e) {
- e.addTrace(pos, "from call site");
+ e.addTrace(positions[pos], "from call site");
throw;
}
- if (!lambda.arg.empty())
+ if (lambda.arg)
env2.values[displ++] = args[0];
/* For each formal argument, get the actual argument. If
@@ -1393,8 +1410,9 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
if (!j) {
if (!i.def) {
throwTypeErrorWithTrace(lambda.pos,
- "function '%1%' called without required argument '%2%'", prettyLambdaName(lambda), i.name,
- pos, "from call site");
+ "function '%1%' called without required argument '%2%'",
+ (lambda.name ? std::string(symbols[lambda.name]) : "anonymous lambda"),
+ i.name, pos, "from call site");
}
env2.values[displ++] = i.def->maybeThunk(*this, env2);
} else {
@@ -1412,11 +1430,12 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
if (!lambda.formals->has(i.name)) {
std::set formalNames;
for (auto & formal : lambda.formals->formals)
- formalNames.insert(formal.name);
+ formalNames.insert(symbols[formal.name]);
throwTypeErrorWithTrace(lambda.pos,
- Suggestions::bestMatches(formalNames, i.name),
- "function '%1%' called with unexpected argument '%2%'", prettyLambdaName(lambda), i.name,
- pos, "from call site");
+ Suggestions::bestMatches(formalNames, symbols[i.name]),
+ "function '%1%' called with unexpected argument '%2%'",
+ (lambda.name ? std::string(symbols[lambda.name]) : "anonymous lambda"),
+ i.name, pos, "from call site");
}
abort(); // can't happen
}
@@ -1430,8 +1449,9 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
lambda.body->eval(*this, env2, vCur);
} catch (Error & e) {
if (loggerSettings.showTrace.get()) {
- addErrorTrace(e, lambda.pos, "while evaluating the '%s' function", prettyLambdaName(lambda));
- if (pos) e.addTrace(pos, "from call site");
+ addErrorTrace(e, lambda.pos, "while evaluating the '%s' function",
+ (lambda.name ? std::string(symbols[lambda.name]) : "anonymous lambda"));
+ addErrorTrace(e, pos, "from call site%s", "");
}
throw;
}
@@ -1450,7 +1470,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
return;
} else {
/* We have all the arguments, so call the primop. */
- Symbol name = vCur.primOp->name;
+ auto name = vCur.primOp->name;
nrPrimOpCalls++;
if (countCalls) primOpCalls[name]++;
@@ -1495,7 +1515,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
for (size_t i = 0; i < argsLeft; ++i)
vArgs[argsDone + i] = args[i];
- Symbol name = primOp->primOp->name;
+ auto name = primOp->primOp->name;
nrPrimOpCalls++;
if (countCalls) primOpCalls[name]++;
@@ -1518,9 +1538,9 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
Value * args2[] = {allocValue(), args[0]};
*args2[0] = vCur;
try {
- callFunction(*functor->value, 2, args2, vCur, *functor->pos);
+ callFunction(*functor->value, 2, args2, vCur, functor->pos);
} catch (Error & e) {
- e.addTrace(pos, "while calling a functor (an attribute set with a '__functor' attribute)");
+ e.addTrace(positions[pos], "while calling a functor (an attribute set with a '__functor' attribute)");
throw;
}
nrArgs--;
@@ -1597,7 +1617,7 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res)
Nix attempted to evaluate a function as a top level expression; in
this case it must have its arguments supplied either by default
values, or passed explicitly with '--arg' or '--argstr'. See
-https://nixos.org/manual/nix/stable/#ss-functions.)", i.name);
+https://nixos.org/manual/nix/stable/#ss-functions.)", symbols[i.name]);
}
}
@@ -1630,8 +1650,8 @@ void ExprAssert::eval(EvalState & state, Env & env, Value & v)
{
if (!state.evalBool(env, cond, pos, "in the condition of the assert statement")) {
std::ostringstream out;
- cond->show(out);
- throwAssertionError(pos, "assertion '%1%' failed", out.str());
+ cond->show(state.symbols, out);
+ state.throwAssertionError(pos, "assertion '%1%' failed", out.str());
}
body->eval(state, env, v);
}
@@ -1724,7 +1744,7 @@ void ExprOpConcatLists::eval(EvalState & state, Env & env, Value & v)
}
-void EvalState::concatLists(Value & v, size_t nrLists, Value * * lists, const Pos & pos, std::string_view errorCtx)
+void EvalState::concatLists(Value & v, size_t nrLists, Value * * lists, const PosIdx pos, std::string_view errorCtx)
{
nrListConcats++;
@@ -1808,14 +1828,14 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
nf = n;
nf += vTmp.fpoint;
} else
- throwEvalError(i_pos, "cannot add %1% to an integer", vTmp);
+ state.throwEvalError(i_pos, "cannot add %1% to an integer", showType(vTmp));
} else if (firstType == nFloat) {
if (vTmp.type() == nInt) {
nf += vTmp.integer;
} else if (vTmp.type() == nFloat) {
nf += vTmp.fpoint;
} else
- throwEvalError(i_pos, "cannot add %1% to a float", vTmp);
+ state.throwEvalError(i_pos, "cannot add %1% to a float", showType(vTmp));
} else {
if (s.empty()) s.reserve(es->size());
/* skip canonization of first path, which would only be not
@@ -1835,7 +1855,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
v.mkFloat(nf);
else if (firstType == nPath) {
if (!context.empty())
- throwEvalError(pos, "a string that refers to a store path cannot be appended to a path");
+ state.throwEvalError(pos, "a string that refers to a store path cannot be appended to a path");
v.mkPath(canonPath(str()));
} else
v.mkStringMove(c_str(), context);
@@ -1844,7 +1864,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
void ExprPos::eval(EvalState & state, Env & env, Value & v)
{
- state.mkPos(v, ptr(&pos));
+ state.mkPos(v, pos);
}
@@ -1864,7 +1884,7 @@ void EvalState::forceValueDeep(Value & v)
try {
recurse(*i.value);
} catch (Error & e) {
- addErrorTrace(e, *i.pos, "while evaluating the attribute '%1%'", i.name);
+ addErrorTrace(e, i.pos, "while evaluating the attribute '%1%'", symbols[i.name]);
throw;
}
}
@@ -1879,7 +1899,7 @@ void EvalState::forceValueDeep(Value & v)
}
-NixInt EvalState::forceInt(Value & v, const Pos & pos, std::string_view errorCtx)
+NixInt EvalState::forceInt(Value & v, const PosIdx pos, std::string_view errorCtx)
{
try {
forceValue(v, pos);
@@ -1887,13 +1907,13 @@ NixInt EvalState::forceInt(Value & v, const Pos & pos, std::string_view errorCtx
throwTypeError("value is %1% while an integer was expected", v);
return v.integer;
} catch (Error & e) {
- e.addTrace(pos, errorCtx);
+ e.addTrace(positions[pos], errorCtx);
throw;
}
}
-NixFloat EvalState::forceFloat(Value & v, const Pos & pos, std::string_view errorCtx)
+NixFloat EvalState::forceFloat(Value & v, const PosIdx pos, std::string_view errorCtx)
{
try {
forceValue(v, pos);
@@ -1903,13 +1923,13 @@ NixFloat EvalState::forceFloat(Value & v, const Pos & pos, std::string_view erro
throwTypeError("value is %1% while a float was expected", v);
return v.fpoint;
} catch (Error & e) {
- e.addTrace(pos, errorCtx);
+ e.addTrace(positions[pos], errorCtx);
throw;
}
}
-bool EvalState::forceBool(Value & v, const Pos & pos, std::string_view errorCtx)
+bool EvalState::forceBool(Value & v, const PosIdx pos, std::string_view errorCtx)
{
try {
forceValue(v, pos);
@@ -1917,7 +1937,7 @@ bool EvalState::forceBool(Value & v, const Pos & pos, std::string_view errorCtx)
throwTypeError("value is %1% while a Boolean was expected", v);
return v.boolean;
} catch (Error & e) {
- e.addTrace(pos, errorCtx);
+ e.addTrace(positions[pos], errorCtx);
throw;
}
}
@@ -1929,20 +1949,20 @@ bool EvalState::isFunctor(Value & fun)
}
-void EvalState::forceFunction(Value & v, const Pos & pos, std::string_view errorCtx)
+void EvalState::forceFunction(Value & v, const PosIdx pos, std::string_view errorCtx)
{
try {
forceValue(v, pos);
if (v.type() != nFunction && !isFunctor(v))
throwTypeError("value is %1% while a function was expected", v);
} catch (Error & e) {
- e.addTrace(pos, errorCtx);
+ e.addTrace(positions[pos], errorCtx);
throw;
}
}
-std::string_view EvalState::forceString(Value & v, const Pos & pos, std::string_view errorCtx)
+std::string_view EvalState::forceString(Value & v, const PosIdx pos, std::string_view errorCtx)
{
try {
forceValue(v, pos);
@@ -1950,7 +1970,7 @@ std::string_view EvalState::forceString(Value & v, const Pos & pos, std::string_
throwTypeError("value is %1% while a string was expected", v);
return v.string.s;
} catch (Error & e) {
- e.addTrace(pos, errorCtx);
+ e.addTrace(positions[pos], errorCtx);
throw;
}
}
@@ -1958,13 +1978,22 @@ std::string_view EvalState::forceString(Value & v, const Pos & pos, std::string_
/* Decode a context string ‘!!’ into a pair . */
-std::pair decodeContext(std::string_view s)
+NixStringContextElem decodeContext(const Store & store, std::string_view s)
{
if (s.at(0) == '!') {
size_t index = s.find("!", 1);
- return {std::string(s.substr(index + 1)), std::string(s.substr(1, index - 1))};
+ return {
+ store.parseStorePath(s.substr(index + 1)),
+ std::string(s.substr(1, index - 1)),
+ };
} else
- return {s.at(0) == '/' ? std::string(s) : std::string(s.substr(1)), ""};
+ return {
+ store.parseStorePath(
+ s.at(0) == '/'
+ ? s
+ : s.substr(1)),
+ "",
+ };
}
@@ -1976,18 +2005,18 @@ void copyContext(const Value & v, PathSet & context)
}
-std::vector> Value::getContext()
+NixStringContext Value::getContext(const Store & store)
{
- std::vector> res;
+ NixStringContext res;
assert(internalType == tString);
if (string.context)
for (const char * * p = string.context; *p; ++p)
- res.push_back(decodeContext(*p));
+ res.push_back(decodeContext(store, *p));
return res;
}
-std::string_view EvalState::forceString(Value & v, PathSet & context, const Pos & pos, std::string_view errorCtx)
+std::string_view EvalState::forceString(Value & v, PathSet & context, const PosIdx pos, std::string_view errorCtx)
{
auto s = forceString(v, pos, errorCtx);
copyContext(v, context);
@@ -1995,7 +2024,7 @@ std::string_view EvalState::forceString(Value & v, PathSet & context, const Pos
}
-std::string_view EvalState::forceStringNoCtx(Value & v, const Pos & pos, std::string_view errorCtx)
+std::string_view EvalState::forceStringNoCtx(Value & v, const PosIdx pos, std::string_view errorCtx)
{
try {
auto s = forceString(v, pos, errorCtx);
@@ -2007,7 +2036,7 @@ std::string_view EvalState::forceStringNoCtx(Value & v, const Pos & pos, std::st
}
return s;
} catch (Error & e) {
- e.addTrace(pos, errorCtx);
+ e.addTrace(positions[pos], errorCtx);
throw;
}
}
@@ -2018,13 +2047,13 @@ bool EvalState::isDerivation(Value & v)
if (v.type() != nAttrs) return false;
Bindings::iterator i = v.attrs->find(sType);
if (i == v.attrs->end()) return false;
- forceValue(*i->value, *i->pos);
+ forceValue(*i->value, i->pos);
if (i->value->type() != nString) return false;
return strcmp(i->value->string.s, "derivation") == 0;
}
-std::optional EvalState::tryAttrsToString(const Pos & pos, Value & v,
+std::optional EvalState::tryAttrsToString(const PosIdx pos, Value & v,
PathSet & context, bool coerceMore, bool copyToStore)
{
auto i = v.attrs->find(sToString);
@@ -2038,7 +2067,7 @@ std::optional EvalState::tryAttrsToString(const Pos & pos, Value &
return {};
}
-BackedStringView EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context,
+BackedStringView EvalState::coerceToString(const PosIdx pos, Value & v, PathSet & context,
bool coerceMore, bool copyToStore, bool canonicalizePath, std::string_view errorCtx)
{
forceValue(v, pos);
@@ -2068,7 +2097,7 @@ BackedStringView EvalState::coerceToString(const Pos & pos, Value & v, PathSet &
}
if (v.type() == nExternal)
- return v.external->coerceToString(pos, context, coerceMore, copyToStore, errorCtx);
+ return v.external->coerceToString(positions[pos], context, coerceMore, copyToStore, errorCtx);
if (coerceMore) {
@@ -2087,7 +2116,7 @@ BackedStringView EvalState::coerceToString(const Pos & pos, Value & v, PathSet &
result += *coerceToString(noPos, *v2, context, coerceMore, copyToStore, canonicalizePath,
"while evaluating one element of the list");
} catch (Error & e) {
- e.addTrace(pos, errorCtx);
+ e.addTrace(positions[pos], errorCtx);
throw;
}
if (n < v.listSize() - 1
@@ -2127,7 +2156,7 @@ std::string EvalState::copyPathToStore(PathSet & context, const Path & path)
}
-Path EvalState::coerceToPath(const Pos & pos, Value & v, PathSet & context, std::string_view errorCtx)
+Path EvalState::coerceToPath(const PosIdx pos, Value & v, PathSet & context, std::string_view errorCtx)
{
auto path = coerceToString(pos, v, context, false, false, true, errorCtx).toOwned();
if (path == "" || path[0] != '/')
@@ -2136,7 +2165,7 @@ Path EvalState::coerceToPath(const Pos & pos, Value & v, PathSet & context, std:
}
-StorePath EvalState::coerceToStorePath(const Pos & pos, Value & v, PathSet & context, std::string_view errorCtx)
+StorePath EvalState::coerceToStorePath(const PosIdx pos, Value & v, PathSet & context, std::string_view errorCtx)
{
auto path = coerceToString(pos, v, context, false, false, true, errorCtx).toOwned();
if (auto storePath = store->maybeParseStorePath(path))
@@ -2145,7 +2174,7 @@ StorePath EvalState::coerceToStorePath(const Pos & pos, Value & v, PathSet & con
}
-bool EvalState::eqValues(Value & v1, Value & v2, const Pos & pos, std::string_view errorCtx)
+bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_view errorCtx)
{
forceValue(v1, noPos);
forceValue(v2, noPos);
@@ -2307,14 +2336,14 @@ void EvalState::printStats()
auto list = topObj.list("functions");
for (auto & i : functionCalls) {
auto obj = list.object();
- if (i.first->name.set())
+ if (i.first->name)
obj.attr("name", (const std::string &) i.first->name);
else
obj.attr("name", nullptr);
- if (i.first->pos) {
- obj.attr("file", (const std::string &) i.first->pos.file);
- obj.attr("line", i.first->pos.line);
- obj.attr("column", i.first->pos.column);
+ if (auto pos = positions[i.first->pos]) {
+ obj.attr("file", (const std::string &) pos.file);
+ obj.attr("line", pos.line);
+ obj.attr("column", pos.column);
}
obj.attr("count", i.second);
}
@@ -2323,10 +2352,10 @@ void EvalState::printStats()
auto list = topObj.list("attributes");
for (auto & i : attrSelects) {
auto obj = list.object();
- if (i.first) {
- obj.attr("file", (const std::string &) i.first.file);
- obj.attr("line", i.first.line);
- obj.attr("column", i.first.column);
+ if (auto pos = positions[i.first]) {
+ obj.attr("file", (const std::string &) pos.file);
+ obj.attr("line", pos.line);
+ obj.attr("column", pos.column);
}
obj.attr("count", i.second);
}
@@ -2343,7 +2372,11 @@ void EvalState::printStats()
std::string ExternalValueBase::coerceToString(const Pos & pos, PathSet & context, bool copyMore, bool copyToStore, std::string_view errorCtx) const
{
- throwTypeErrorWithTrace("cannot coerce %1% to a string", showType(), pos, errorCtx);
+ auto e = TypeError(ErrorInfo {
+ .msg = hintfmt("cannot coerce %1% to a string", showType())
+ });
+ e.addTrace(pos, errorCtx);
+ throw e;
}
diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh
index fd0961bdc..4c7d92dbc 100644
--- a/src/libexpr/eval.hh
+++ b/src/libexpr/eval.hh
@@ -23,14 +23,14 @@ class StorePath;
enum RepairFlag : bool;
-typedef void (* PrimOpFun) (EvalState & state, const Pos & pos, Value * * args, Value & v);
+typedef void (* PrimOpFun) (EvalState & state, const PosIdx pos, Value * * args, Value & v);
struct PrimOp
{
PrimOpFun fun;
size_t arity;
- Symbol name;
+ std::string name;
std::vector args;
const char * doc = nullptr;
};
@@ -53,7 +53,8 @@ void copyContext(const Value & v, PathSet & context);
typedef std::map SrcToStore;
-std::ostream & operator << (std::ostream & str, const Value & v);
+std::ostream & printValue(const EvalState & state, std::ostream & str, const Value & v);
+std::string printValue(const EvalState & state, const Value & v);
typedef std::pair SearchPathElem;
@@ -73,12 +74,15 @@ class EvalState
{
public:
SymbolTable symbols;
+ PosTable positions;
+
+ static inline std::string derivationNixPath = "//builtin/derivation.nix";
const Symbol sWith, sOutPath, sDrvPath, sType, sMeta, sName, sValue,
sSystem, sOverrides, sOutputs, sOutputName, sIgnoreNulls,
sFile, sLine, sColumn, sFunctor, sToString,
sRight, sWrong, sStructuredAttrs, sBuilder, sArgs,
- sContentAddressed,
+ sContentAddressed, sImpure,
sOutputHash, sOutputHashAlgo, sOutputHashMode,
sRecurseForDerivations,
sDescription, sSelf, sEpsilon, sStartSet, sOperator, sKey, sPath,
@@ -149,12 +153,6 @@ public:
std::shared_ptr buildStore = nullptr);
~EvalState();
- void requireExperimentalFeatureOnEvaluation(
- const ExperimentalFeature &,
- const std::string_view fName,
- const Pos & pos
- );
-
void addToSearchPath(const std::string & s);
SearchPath getSearchPath() { return searchPath; }
@@ -211,7 +209,7 @@ public:
/* Look up a file in the search path. */
Path findFile(const std::string_view path);
- Path findFile(SearchPath & searchPath, const std::string_view path, const Pos & pos = noPos);
+ Path findFile(SearchPath & searchPath, const std::string_view path, const PosIdx pos = noPos);
/* If the specified search path element is a URI, download it. */
std::pair resolveSearchPathElem(const SearchPathElem & elem);
@@ -222,14 +220,15 @@ public:
/* Evaluation the expression, then verify that it has the expected
type. */
- inline bool evalBool(Env & env, Expr * e, const Pos & pos, std::string_view errorCtx);
- inline void evalAttrs(Env & env, Expr * e, Value & v, const Pos & pos, std::string_view errorCtx);
+ inline bool evalBool(Env & env, Expr * e);
+ inline bool evalBool(Env & env, Expr * e, const PosIdx pos, std::string_view errorCtx);
+ inline void evalAttrs(Env & env, Expr * e, Value & v, const PosIdx pos, std::string_view errorCtx);
/* If `v' is a thunk, enter it and overwrite `v' with the result
of the evaluation of the thunk. If `v' is a delayed function
application, call the function and overwrite `v' with the
result. Otherwise, this is a no-op. */
- inline void forceValue(Value & v, const Pos & pos);
+ inline void forceValue(Value & v, const PosIdx pos);
template
inline void forceValue(Value & v, Callable getPos);
@@ -239,33 +238,61 @@ public:
void forceValueDeep(Value & v);
/* Force `v', and then verify that it has the expected type. */
- NixInt forceInt(Value & v, const Pos & pos, std::string_view errorCtx);
- NixFloat forceFloat(Value & v, const Pos & pos, std::string_view errorCtx);
- bool forceBool(Value & v, const Pos & pos, std::string_view errorCtx);
+ NixInt forceInt(Value & v, const PosIdx pos, std::string_view errorCtx);
+ NixFloat forceFloat(Value & v, const PosIdx pos, std::string_view errorCtx);
+ bool forceBool(Value & v, const PosIdx pos, std::string_view errorCtx);
- void forceAttrs(Value & v, const Pos & pos, std::string_view errorCtx);
+ void forceAttrs(Value & v, const PosIdx pos, std::string_view errorCtx);
template
inline void forceAttrs(Value & v, Callable getPos, std::string_view errorCtx);
- inline void forceList(Value & v, const Pos & pos, std::string_view errorCtx);
- void forceFunction(Value & v, const Pos & pos, std::string_view errorCtx); // either lambda or primop
- std::string_view forceString(Value & v, const Pos & pos, std::string_view errorCtx);
- std::string_view forceString(Value & v, PathSet & context, const Pos & pos, std::string_view errorCtx);
- std::string_view forceStringNoCtx(Value & v, const Pos & pos, std::string_view errorCtx);
+ inline void forceList(Value & v, const PosIdx pos, std::string_view errorCtx);
+ void forceFunction(Value & v, const PosIdx pos, std::string_view errorCtx); // either lambda or primop
+ std::string_view forceString(Value & v, const PosIdx pos, std::string_view errorCtx);
+ std::string_view forceString(Value & v, PathSet & context, const PosIdx pos, std::string_view errorCtx);
+ std::string_view forceStringNoCtx(Value & v, const PosIdx pos, std::string_view errorCtx);
+ [[gnu::noinline, gnu::noreturn]] void throwEvalError(const PosIdx pos, const char * s) const;
+ [[gnu::noinline, gnu::noreturn]] void throwEvalError(const PosIdx pos, const char * s, const Value & v) const;
+ [[gnu::noinline, gnu::noreturn]] void throwEvalError(const char * s, const std::string_view s2) const;
+ [[gnu::noinline, gnu::noreturn]] void throwEvalError(const PosIdx pos, const Suggestions & suggestions, const char * s, const std::string_view s2) const;
+ [[gnu::noinline, gnu::noreturn]] void throwEvalError(const PosIdx pos, const char * s, const std::string_view s2) const;
+ [[gnu::noinline, gnu::noreturn]] void throwEvalError(const char * s, const std::string_view s2, const std::string_view s3) const;
+ [[gnu::noinline, gnu::noreturn]] void throwEvalError(const PosIdx pos, const char * s, const std::string & s2, const std::string & s3) const;
+ [[gnu::noinline, gnu::noreturn]] void throwEvalError(const PosIdx p1, const char * s, const Symbol sym, const PosIdx p2) const;
+ [[gnu::noinline, gnu::noreturn]] void throwEvalError(const char * s, const Value & v) const;
+ [[gnu::noinline, gnu::noreturn]] void throwTypeError(const PosIdx pos, const char * s) const;
+ [[gnu::noinline, gnu::noreturn]] void throwTypeError(const PosIdx pos, const char * s, const Value & v) const;
+ [[gnu::noinline, gnu::noreturn]] void throwTypeError(const PosIdx pos, const char * s, const ExprLambda & fun, const Symbol s2) const;
+ [[gnu::noinline, gnu::noreturn]] void throwTypeError(const PosIdx pos, const Suggestions & suggestions, const char * s, const ExprLambda & fun, const Symbol s2) const;
+ [[gnu::noinline, gnu::noreturn]] void throwTypeError(const char * s, const Value & v) const;
+ [[gnu::noinline, gnu::noreturn]] void throwTypeErrorWithTrace(const PosIdx, const char*, std::string_view, const nix::Symbol&, const PosIdx, std::string_view) const;
+ [[gnu::noinline, gnu::noreturn]] void throwTypeErrorWithTrace(const PosIdx, const nix::Suggestions&, const char*, std::string_view, const nix::Symbol&, const PosIdx, std::string_view) const;
+ [[gnu::noinline, gnu::noreturn]] void throwTypeErrorWithTrace(const char*, std::string_view, const Pos &, std::string_view) const;
+ [[gnu::noinline, gnu::noreturn]] void throwTypeErrorWithTrace(const char*, std::string_view, const PosIdx, std::string_view) const;
+ [[gnu::noinline, gnu::noreturn]] void throwEvalErrorWithTrace(const char*, std::string_view, const PosIdx, std::string_view) const;
+ [[gnu::noinline, gnu::noreturn]] void throwEvalErrorWithTrace(const char*, std::string_view, std::string_view, nix::PosIdx, std::string_view) const;
+ [[gnu::noinline, gnu::noreturn]] void throwAssertionError(const PosIdx pos, const char * s, const std::string & s1) const;
+ [[gnu::noinline, gnu::noreturn]] void throwUndefinedVarError(const PosIdx pos, const char * s, const std::string & s1) const;
+ [[gnu::noinline, gnu::noreturn]] void throwMissingArgumentError(const PosIdx pos, const char * s, const std::string & s1) const;
+
+ [[gnu::noinline]] void addErrorTrace(Error & e, const char * s, const std::string & s2) const;
+ [[gnu::noinline]] void addErrorTrace(Error & e, const PosIdx pos, const char * s, const std::string & s2) const;
+
+public:
/* Return true iff the value `v' denotes a derivation (i.e. a
set with attribute `type = "derivation"'). */
bool isDerivation(Value & v);
- std::optional tryAttrsToString(const Pos & pos, Value & v,
+ std::optional tryAttrsToString(const PosIdx pos, Value & v,
PathSet & context, bool coerceMore = false, bool copyToStore = true);
/* String coercion. Converts strings, paths and derivations to a
string. If `coerceMore' is set, also converts nulls, integers,
booleans and lists to a string. If `copyToStore' is set,
referenced paths are copied to the Nix store as a side effect. */
- BackedStringView coerceToString(const Pos & pos, Value & v, PathSet & context,
+ BackedStringView coerceToString(const PosIdx pos, Value & v, PathSet & context,
bool coerceMore = false, bool copyToStore = true,
bool canonicalizePath = true,
std::string_view errorCtx = "");
@@ -275,10 +302,10 @@ public:
/* Path coercion. Converts strings, paths and derivations to a
path. The result is guaranteed to be a canonicalised, absolute
path. Nothing is copied to the store. */
- Path coerceToPath(const Pos & pos, Value & v, PathSet & context, std::string_view errorCtx);
+ Path coerceToPath(const PosIdx pos, Value & v, PathSet & context, std::string_view errorCtx);
/* Like coerceToPath, but the result must be a store path. */
- StorePath coerceToStorePath(const Pos & pos, Value & v, PathSet & context, std::string_view errorCtx);
+ StorePath coerceToStorePath(const PosIdx pos, Value & v, PathSet & context, std::string_view errorCtx);
public:
@@ -311,7 +338,7 @@ public:
struct Doc
{
Pos pos;
- std::optional name;
+ std::optional name;
size_t arity;
std::vector args;
const char * doc;
@@ -334,14 +361,14 @@ public:
/* Do a deep equality test between two values. That is, list
elements and attributes are compared recursively. */
- bool eqValues(Value & v1, Value & v2, const Pos & pos, std::string_view errorCtx);
+ bool eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_view errorCtx);
bool isFunctor(Value & fun);
// FIXME: use std::span
- void callFunction(Value & fun, size_t nrArgs, Value * * args, Value & vRes, const Pos & pos);
+ void callFunction(Value & fun, size_t nrArgs, Value * * args, Value & vRes, const PosIdx pos);
- void callFunction(Value & fun, Value & arg, Value & vRes, const Pos & pos)
+ void callFunction(Value & fun, Value & arg, Value & vRes, const PosIdx pos)
{
Value * args[] = {&arg};
callFunction(fun, 1, args, vRes, pos);
@@ -355,7 +382,7 @@ public:
inline Value * allocValue();
inline Env & allocEnv(size_t size);
- Value * allocAttr(Value & vAttrs, const Symbol & name);
+ Value * allocAttr(Value & vAttrs, Symbol name);
Value * allocAttr(Value & vAttrs, std::string_view name);
Bindings * allocBindings(size_t capacity);
@@ -367,9 +394,9 @@ public:
void mkList(Value & v, size_t length);
void mkThunk_(Value & v, Expr * expr);
- void mkPos(Value & v, ptr pos);
+ void mkPos(Value & v, PosIdx pos);
- void concatLists(Value & v, size_t nrLists, Value * * lists, const Pos & pos, std::string_view errorCtx);
+ void concatLists(Value & v, size_t nrLists, Value * * lists, const PosIdx pos, std::string_view errorCtx);
/* Print statistics. */
void printStats();
@@ -397,7 +424,7 @@ private:
bool countCalls;
- typedef std::map PrimOpCalls;
+ typedef std::map PrimOpCalls;
PrimOpCalls primOpCalls;
typedef std::map FunctionCalls;
@@ -405,7 +432,7 @@ private:
void incrFunctionCall(ExprLambda * fun);
- typedef std::map AttrSelects;
+ typedef std::map AttrSelects;
AttrSelects attrSelects;
friend struct ExprOpUpdate;
@@ -416,9 +443,9 @@ private:
friend struct ExprFloat;
friend struct ExprPath;
friend struct ExprSelect;
- friend void prim_getAttr(EvalState & state, const Pos & pos, Value * * args, Value & v);
- friend void prim_match(EvalState & state, const Pos & pos, Value * * args, Value & v);
- friend void prim_split(EvalState & state, const Pos & pos, Value * * args, Value & v);
+ friend void prim_getAttr(EvalState & state, const PosIdx pos, Value * * args, Value & v);
+ friend void prim_match(EvalState & state, const PosIdx pos, Value * * args, Value & v);
+ friend void prim_split(EvalState & state, const PosIdx pos, Value * * args, Value & v);
friend struct Value;
};
@@ -430,7 +457,7 @@ std::string showType(const Value & v);
/* Decode a context string ‘!!’ into a pair . */
-std::pair decodeContext(std::string_view s);
+NixStringContextElem decodeContext(const Store & store, std::string_view s);
/* If `path' refers to a directory, then append "/default.nix". */
Path resolveExprPath(Path path);
diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc
index 8427cf213..29fd51dd1 100644
--- a/src/libexpr/flake/flake.cc
+++ b/src/libexpr/flake/flake.cc
@@ -72,7 +72,7 @@ static std::tuple fetchOrSubstituteTree(
return {std::move(tree), resolvedRef, lockedRef};
}
-static void forceTrivialValue(EvalState & state, Value & value, const Pos & pos)
+static void forceTrivialValue(EvalState & state, Value & value, const PosIdx pos)
{
if (value.isThunk() && value.isTrivial())
state.forceValue(value, pos);
@@ -80,20 +80,20 @@ static void forceTrivialValue(EvalState & state, Value & value, const Pos & pos)
static void expectType(EvalState & state, ValueType type,
- Value & value, const Pos & pos)
+ Value & value, const PosIdx pos)
{
forceTrivialValue(state, value, pos);
if (value.type() != type)
throw Error("expected %s but got %s at %s",
- showType(type), showType(value.type()), pos);
+ showType(type), showType(value.type()), state.positions[pos]);
}
static std::map parseFlakeInputs(
- EvalState & state, Value * value, const Pos & pos,
+ EvalState & state, Value * value, const PosIdx pos,
const std::optional & baseDir, InputPath lockRootPath);
static FlakeInput parseFlakeInput(EvalState & state,
- const std::string & inputName, Value * value, const Pos & pos,
+ const std::string & inputName, Value * value, const PosIdx pos,
const std::optional & baseDir, InputPath lockRootPath)
{
expectType(state, nAttrs, *value, pos);
@@ -111,37 +111,39 @@ static FlakeInput parseFlakeInput(EvalState & state,
for (nix::Attr attr : *(value->attrs)) {
try {
if (attr.name == sUrl) {
- expectType(state, nString, *attr.value, *attr.pos);
+ expectType(state, nString, *attr.value, attr.pos);
url = attr.value->string.s;
attrs.emplace("url", *url);
} else if (attr.name == sFlake) {
- expectType(state, nBool, *attr.value, *attr.pos);
+ expectType(state, nBool, *attr.value, attr.pos);
input.isFlake = attr.value->boolean;
} else if (attr.name == sInputs) {
- input.overrides = parseFlakeInputs(state, attr.value, *attr.pos, baseDir, lockRootPath);
+ input.overrides = parseFlakeInputs(state, attr.value, attr.pos, baseDir, lockRootPath);
} else if (attr.name == sFollows) {
- expectType(state, nString, *attr.value, *attr.pos);
+ expectType(state, nString, *attr.value, attr.pos);
auto follows(parseInputPath(attr.value->string.s));
follows.insert(follows.begin(), lockRootPath.begin(), lockRootPath.end());
input.follows = follows;
} else {
switch (attr.value->type()) {
case nString:
- attrs.emplace(attr.name, attr.value->string.s);
+ attrs.emplace(state.symbols[attr.name], attr.value->string.s);
break;
case nBool:
- attrs.emplace(attr.name, Explicit { attr.value->boolean });
+ attrs.emplace(state.symbols[attr.name], Explicit { attr.value->boolean });
break;
case nInt:
- attrs.emplace(attr.name, (long unsigned int)attr.value->integer);
+ attrs.emplace(state.symbols[attr.name], (long unsigned int)attr.value->integer);
break;
default:
throw TypeError("flake input attribute '%s' is %s while a string, Boolean, or integer is expected",
- attr.name, showType(*attr.value));
+ state.symbols[attr.name], showType(*attr.value));
}
}
} catch (Error & e) {
- e.addTrace(*attr.pos, hintfmt("in flake attribute '%s'", attr.name));
+ e.addTrace(
+ state.positions[attr.pos],
+ hintfmt("in flake attribute '%s'", state.symbols[attr.name]));
throw;
}
}
@@ -150,13 +152,13 @@ static FlakeInput parseFlakeInput(EvalState & state,
try {
input.ref = FlakeRef::fromAttrs(attrs);
} catch (Error & e) {
- e.addTrace(pos, hintfmt("in flake input"));
+ e.addTrace(state.positions[pos], hintfmt("in flake input"));
throw;
}
else {
attrs.erase("url");
if (!attrs.empty())
- throw Error("unexpected flake input attribute '%s', at %s", attrs.begin()->first, pos);
+ throw Error("unexpected flake input attribute '%s', at %s", attrs.begin()->first, state.positions[pos]);
if (url)
input.ref = parseFlakeRef(*url, baseDir, true, input.isFlake);
}
@@ -168,7 +170,7 @@ static FlakeInput parseFlakeInput(EvalState & state,
}
static std::map parseFlakeInputs(
- EvalState & state, Value * value, const Pos & pos,
+ EvalState & state, Value * value, const PosIdx pos,
const std::optional & baseDir, InputPath lockRootPath)
{
std::map inputs;
@@ -176,11 +178,11 @@ static std::map parseFlakeInputs(
expectType(state, nAttrs, *value, pos);
for (nix::Attr & inputAttr : *(*value).attrs) {
- inputs.emplace(inputAttr.name,
+ inputs.emplace(state.symbols[inputAttr.name],
parseFlakeInput(state,
- inputAttr.name,
+ state.symbols[inputAttr.name],
inputAttr.value,
- *inputAttr.pos,
+ inputAttr.pos,
baseDir,
lockRootPath));
}
@@ -218,28 +220,28 @@ static Flake getFlake(
Value vInfo;
state.evalFile(flakeFile, vInfo, true); // FIXME: symlink attack
- expectType(state, nAttrs, vInfo, Pos(foFile, state.symbols.create(flakeFile), 0, 0));
+ expectType(state, nAttrs, vInfo, state.positions.add({flakeFile, foFile}, 0, 0));
if (auto description = vInfo.attrs->get(state.sDescription)) {
- expectType(state, nString, *description->value, *description->pos);
+ expectType(state, nString, *description->value, description->pos);
flake.description = description->value->string.s;
}
auto sInputs = state.symbols.create("inputs");
if (auto inputs = vInfo.attrs->get(sInputs))
- flake.inputs = parseFlakeInputs(state, inputs->value, *inputs->pos, flakeDir, lockRootPath);
+ flake.inputs = parseFlakeInputs(state, inputs->value, inputs->pos, flakeDir, lockRootPath);
auto sOutputs = state.symbols.create("outputs");
if (auto outputs = vInfo.attrs->get(sOutputs)) {
- expectType(state, nFunction, *outputs->value, *outputs->pos);
+ expectType(state, nFunction, *outputs->value, outputs->pos);
if (outputs->value->isLambda() && outputs->value->lambda.fun->hasFormals()) {
for (auto & formal : outputs->value->lambda.fun->formals->formals) {
if (formal.name != state.sSelf)
- flake.inputs.emplace(formal.name, FlakeInput {
- .ref = parseFlakeRef(formal.name)
+ flake.inputs.emplace(state.symbols[formal.name], FlakeInput {
+ .ref = parseFlakeRef(state.symbols[formal.name])
});
}
}
@@ -250,35 +252,41 @@ static Flake getFlake(
auto sNixConfig = state.symbols.create("nixConfig");
if (auto nixConfig = vInfo.attrs->get(sNixConfig)) {
- expectType(state, nAttrs, *nixConfig->value, *nixConfig->pos);
+ expectType(state, nAttrs, *nixConfig->value, nixConfig->pos);
for (auto & setting : *nixConfig->value->attrs) {
- forceTrivialValue(state, *setting.value, *setting.pos);
+ forceTrivialValue(state, *setting.value, setting.pos);
if (setting.value->type() == nString)
- flake.config.settings.insert({setting.name, std::string(state.forceStringNoCtx(*setting.value, *setting.pos, ""))});
+ flake.config.settings.emplace(
+ state.symbols[setting.name],
+ std::string(state.forceStringNoCtx(*setting.value, setting.pos, "")));
else if (setting.value->type() == nPath) {
PathSet emptyContext = {};
flake.config.settings.emplace(
- setting.name,
- state.coerceToString(*setting.pos, *setting.value, emptyContext, false, true, true, "") .toOwned());
+ state.symbols[setting.name],
+ state.coerceToString(setting.pos, *setting.value, emptyContext, false, true, true, "") .toOwned());
}
else if (setting.value->type() == nInt)
- flake.config.settings.insert({setting.name, state.forceInt(*setting.value, *setting.pos, "")});
+ flake.config.settings.emplace(
+ state.symbols[setting.name],
+ state.forceInt(*setting.value, setting.pos, ""));
else if (setting.value->type() == nBool)
- flake.config.settings.insert({setting.name, Explicit { state.forceBool(*setting.value, *setting.pos, "") }});
+ flake.config.settings.emplace(
+ state.symbols[setting.name],
+ Explicit { state.forceBool(*setting.value, setting.pos, "") });
else if (setting.value->type() == nList) {
std::vector ss;
for (auto elem : setting.value->listItems()) {
if (elem->type() != nString)
throw TypeError("list element in flake configuration setting '%s' is %s while a string is expected",
- setting.name, showType(*setting.value));
- ss.emplace_back(state.forceStringNoCtx(*elem, *setting.pos, ""));
+ state.symbols[setting.name], showType(*setting.value));
+ ss.emplace_back(state.forceStringNoCtx(*elem, setting.pos, ""));
}
- flake.config.settings.insert({setting.name, ss});
+ flake.config.settings.emplace(state.symbols[setting.name], ss);
}
else
throw TypeError("flake configuration setting '%s' is %s",
- setting.name, showType(*setting.value));
+ state.symbols[setting.name], showType(*setting.value));
}
}
@@ -288,7 +296,7 @@ static Flake getFlake(
attr.name != sOutputs &&
attr.name != sNixConfig)
throw Error("flake '%s' has an unsupported attribute '%s', at %s",
- lockedRef, attr.name, *attr.pos);
+ lockedRef, state.symbols[attr.name], state.positions[attr.pos]);
}
return flake;
@@ -704,14 +712,12 @@ void callFlake(EvalState & state,
state.callFunction(*vTmp2, *vRootSubdir, vRes, noPos);
}
-static void prim_getFlake(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_getFlake(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
- state.requireExperimentalFeatureOnEvaluation(Xp::Flakes, "builtins.getFlake", pos);
-
std::string flakeRefS(state.forceStringNoCtx(*args[0], pos, "while evaluating the argument passed to builtins.getFlake"));
auto flakeRef = parseFlakeRef(flakeRefS, {}, true);
if (evalSettings.pureEval && !flakeRef.input.isLocked())
- throw Error("cannot call 'getFlake' on unlocked flake reference '%s', at %s (use --impure to override)", flakeRefS, pos);
+ throw Error("cannot call 'getFlake' on unlocked flake reference '%s', at %s (use --impure to override)", flakeRefS, state.positions[pos]);
callFlake(state,
lockFlake(state, flakeRef,
@@ -723,7 +729,30 @@ static void prim_getFlake(EvalState & state, const Pos & pos, Value * * args, Va
v);
}
-static RegisterPrimOp r2("__getFlake", 1, prim_getFlake);
+static RegisterPrimOp r2({
+ .name = "__getFlake",
+ .args = {"args"},
+ .doc = R"(
+ Fetch a flake from a flake reference, and return its output attributes and some metadata. For example:
+
+ ```nix
+ (builtins.getFlake "nix/55bc52401966fbffa525c574c14f67b00bc4fb3a").packages.x86_64-linux.nix
+ ```
+
+ Unless impure evaluation is allowed (`--impure`), the flake reference
+ must be "locked", e.g. contain a Git revision or content hash. An
+ example of an unlocked usage is:
+
+ ```nix
+ (builtins.getFlake "github:edolstra/dwarffs").rev
+ ```
+
+ This function is only available if you enable the experimental feature
+ `flakes`.
+ )",
+ .fun = prim_getFlake,
+ .experimentalFeature = Xp::Flakes,
+});
}
diff --git a/src/libexpr/function-trace.hh b/src/libexpr/function-trace.hh
index 472f2045e..e9a2526bd 100644
--- a/src/libexpr/function-trace.hh
+++ b/src/libexpr/function-trace.hh
@@ -8,7 +8,7 @@ namespace nix {
struct FunctionCallTrace
{
- const Pos & pos;
+ const Pos pos;
FunctionCallTrace(const Pos & pos);
~FunctionCallTrace();
};
diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc
index 932ab7f1d..f1a59c379 100644
--- a/src/libexpr/get-drvs.cc
+++ b/src/libexpr/get-drvs.cc
@@ -1,6 +1,7 @@
#include "get-drvs.hh"
#include "util.hh"
#include "eval-inline.hh"
+#include "derivations.hh"
#include "store-api.hh"
#include "path-with-outputs.hh"
@@ -60,7 +61,7 @@ std::string DrvInfo::querySystem() const
{
if (system == "" && attrs) {
auto i = attrs->find(state->sSystem);
- system = i == attrs->end() ? "unknown" : state->forceStringNoCtx(*i->value, *i->pos, "while evaluating the 'system' attribute of a derivation");
+ system = i == attrs->end() ? "unknown" : state->forceStringNoCtx(*i->value, i->pos, "while evaluating the 'system' attribute of a derivation");
}
return system;
}
@@ -74,7 +75,7 @@ std::optional DrvInfo::queryDrvPath() const
if (i == attrs->end())
drvPath = {std::nullopt};
else
- drvPath = {state->coerceToStorePath(*i->pos, *i->value, context, "while evaluating the 'drvPath' attribute of a derivation")};
+ drvPath = {state->coerceToStorePath(i->pos, *i->value, context, "while evaluating the 'drvPath' attribute of a derivation")};
}
return drvPath.value_or(std::nullopt);
}
@@ -94,7 +95,7 @@ StorePath DrvInfo::queryOutPath() const
Bindings::iterator i = attrs->find(state->sOutPath);
PathSet context;
if (i != attrs->end())
- outPath = state->coerceToStorePath(*i->pos, *i->value, context, "while evaluating the output path of a derivation");
+ outPath = state->coerceToStorePath(i->pos, *i->value, context, "while evaluating the output path of a derivation");
}
if (!outPath)
throw UnimplementedError("CA derivations are not yet supported");
@@ -108,23 +109,23 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool withPaths, bool onlyOutputsToInstall
/* Get the ‘outputs’ list. */
Bindings::iterator i;
if (attrs && (i = attrs->find(state->sOutputs)) != attrs->end()) {
- state->forceList(*i->value, *i->pos, "while evaluating the 'outputs' attribute of a derivation");
+ state->forceList(*i->value, i->pos, "while evaluating the 'outputs' attribute of a derivation");
/* For each output... */
for (auto elem : i->value->listItems()) {
- std::string output(state->forceStringNoCtx(*elem, *i->pos, "while evaluating the name of an output of a derivation"));
+ std::string output(state->forceStringNoCtx(*elem, i->pos, "while evaluating the name of an output of a derivation"));
if (withPaths) {
/* Evaluate the corresponding set. */
Bindings::iterator out = attrs->find(state->symbols.create(output));
if (out == attrs->end()) continue; // FIXME: throw error?
- state->forceAttrs(*out->value, *i->pos, "while evaluating an output of a derivation");
+ state->forceAttrs(*out->value, i->pos, "while evaluating an output of a derivation");
/* And evaluate its ‘outPath’ attribute. */
Bindings::iterator outPath = out->value->attrs->find(state->sOutPath);
if (outPath == out->value->attrs->end()) continue; // FIXME: throw error?
PathSet context;
- outputs.emplace(output, state->coerceToStorePath(*outPath->pos, *outPath->value, context, "while evaluating an output path of a derivation"));
+ outputs.emplace(output, state->coerceToStorePath(outPath->pos, *outPath->value, context, "while evaluating an output path of a derivation"));
} else
outputs.emplace(output, std::nullopt);
}
@@ -167,7 +168,7 @@ Bindings * DrvInfo::getMeta()
if (!attrs) return 0;
Bindings::iterator a = attrs->find(state->sMeta);
if (a == attrs->end()) return 0;
- state->forceAttrs(*a->value, *a->pos, "while evaluating the 'meta' attribute of a derivation");
+ state->forceAttrs(*a->value, a->pos, "while evaluating the 'meta' attribute of a derivation");
meta = a->value->attrs;
return meta;
}
@@ -178,7 +179,7 @@ StringSet DrvInfo::queryMetaNames()
StringSet res;
if (!getMeta()) return res;
for (auto & i : *meta)
- res.insert(i.name);
+ res.emplace(state->symbols[i.name]);
return res;
}
@@ -268,7 +269,7 @@ void DrvInfo::setMeta(const std::string & name, Value * v)
{
getMeta();
auto attrs = state->buildBindings(1 + (meta ? meta->size() : 0));
- Symbol sym = state->symbols.create(name);
+ auto sym = state->symbols.create(name);
if (meta)
for (auto i : *meta)
if (i.name != sym)
@@ -355,11 +356,11 @@ static void getDerivations(EvalState & state, Value & vIn,
there are names clashes between derivations, the derivation
bound to the attribute with the "lower" name should take
precedence). */
- for (auto & i : v.attrs->lexicographicOrder()) {
- debug("evaluating attribute '%1%'", i->name);
- if (!std::regex_match(std::string(i->name), attrRegex))
+ for (auto & i : v.attrs->lexicographicOrder(state.symbols)) {
+ debug("evaluating attribute '%1%'", state.symbols[i->name]);
+ if (!std::regex_match(std::string(state.symbols[i->name]), attrRegex))
continue;
- std::string pathPrefix2 = addToPath(pathPrefix, i->name);
+ std::string pathPrefix2 = addToPath(pathPrefix, state.symbols[i->name]);
if (combineChannels)
getDerivations(state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
else if (getDerivation(state, *i->value, pathPrefix2, drvs, done, ignoreAssertionFailures)) {
@@ -368,7 +369,7 @@ static void getDerivations(EvalState & state, Value & vIn,
`recurseForDerivations = true' attribute. */
if (i->value->type() == nAttrs) {
Bindings::iterator j = i->value->attrs->find(state.sRecurseForDerivations);
- if (j != i->value->attrs->end() && state.forceBool(*j->value, *j->pos, "while evaluating the attribute `recurseForDerivations`"))
+ if (j != i->value->attrs->end() && state.forceBool(*j->value, j->pos, "while evaluating the attribute `recurseForDerivations`"))
getDerivations(state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
}
}
diff --git a/src/libexpr/lexer.l b/src/libexpr/lexer.l
index e276b0467..4c28b976e 100644
--- a/src/libexpr/lexer.l
+++ b/src/libexpr/lexer.l
@@ -28,6 +28,13 @@ using namespace nix;
namespace nix {
+static inline PosIdx makeCurPos(const YYLTYPE & loc, ParseData * data)
+{
+ return data->state.positions.add(data->origin, loc.first_line, loc.first_column);
+}
+
+#define CUR_POS makeCurPos(*yylloc, data)
+
// backup to recover from yyless(0)
YYLTYPE prev_yylloc;
@@ -37,7 +44,6 @@ static void initLoc(YYLTYPE * loc)
loc->first_column = loc->last_column = 1;
}
-
static void adjustLoc(YYLTYPE * loc, const char * s, size_t len)
{
prev_yylloc = *loc;
@@ -147,14 +153,20 @@ or { return OR_KW; }
try {
yylval->n = boost::lexical_cast(yytext);
} catch (const boost::bad_lexical_cast &) {
- throw ParseError("invalid integer '%1%'", yytext);
+ throw ParseError({
+ .msg = hintfmt("invalid integer '%1%'", yytext),
+ .errPos = data->state.positions[CUR_POS],
+ });
}
return INT;
}
{FLOAT} { errno = 0;
yylval->nf = strtod(yytext, 0);
if (errno != 0)
- throw ParseError("invalid float '%1%'", yytext);
+ throw ParseError({
+ .msg = hintfmt("invalid float '%1%'", yytext),
+ .errPos = data->state.positions[CUR_POS],
+ });
return FLOAT;
}
@@ -280,7 +292,10 @@ or { return OR_KW; }
{ANY} |
<> {
- throw ParseError("path has a trailing slash");
+ throw ParseError({
+ .msg = hintfmt("path has a trailing slash"),
+ .errPos = data->state.positions[CUR_POS],
+ });
}
{SPATH} { yylval->path = {yytext, (size_t) yyleng}; return SPATH; }
diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc
index a2def65a6..c529fdc89 100644
--- a/src/libexpr/nixexpr.cc
+++ b/src/libexpr/nixexpr.cc
@@ -1,5 +1,7 @@
#include "nixexpr.hh"
#include "derivations.hh"
+#include "eval.hh"
+#include "symbol-table.hh"
#include "util.hh"
#include
@@ -10,12 +12,6 @@ namespace nix {
/* Displaying abstract syntax trees. */
-std::ostream & operator << (std::ostream & str, const Expr & e)
-{
- e.show(str);
- return str;
-}
-
static void showString(std::ostream & str, std::string_view s)
{
str << '"';
@@ -28,8 +24,10 @@ static void showString(std::ostream & str, std::string_view s)
str << '"';
}
-static void showId(std::ostream & str, std::string_view s)
+std::ostream & operator <<(std::ostream & str, const SymbolStr & symbol)
{
+ std::string_view s = symbol;
+
if (s.empty())
str << "\"\"";
else if (s == "if") // FIXME: handle other keywords
@@ -38,7 +36,7 @@ static void showId(std::ostream & str, std::string_view s)
char c = s[0];
if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_')) {
showString(str, s);
- return;
+ return str;
}
for (auto c : s)
if (!((c >= 'a' && c <= 'z') ||
@@ -46,89 +44,104 @@ static void showId(std::ostream & str, std::string_view s)
(c >= '0' && c <= '9') ||
c == '_' || c == '\'' || c == '-')) {
showString(str, s);
- return;
+ return str;
}
str << s;
}
-}
-
-std::ostream & operator << (std::ostream & str, const Symbol & sym)
-{
- showId(str, *sym.s);
return str;
}
-void Expr::show(std::ostream & str) const
+void Expr::show(const SymbolTable & symbols, std::ostream & str) const
{
abort();
}
-void ExprInt::show(std::ostream & str) const
+void ExprInt::show(const SymbolTable & symbols, std::ostream & str) const
{
str << n;
}
-void ExprFloat::show(std::ostream & str) const
+void ExprFloat::show(const SymbolTable & symbols, std::ostream & str) const
{
str << nf;
}
-void ExprString::show(std::ostream & str) const
+void ExprString::show(const SymbolTable & symbols, std::ostream & str) const
{
showString(str, s);
}
-void ExprPath::show(std::ostream & str) const
+void ExprPath::show(const SymbolTable & symbols, std::ostream & str) const
{
str << s;
}
-void ExprVar::show(std::ostream & str) const
+void ExprVar::show(const SymbolTable & symbols, std::ostream & str) const
{
- str << name;
+ str << symbols[name];
}
-void ExprSelect::show(std::ostream & str) const
+void ExprSelect::show(const SymbolTable & symbols, std::ostream & str) const
{
- str << "(" << *e << ")." << showAttrPath(attrPath);
- if (def) str << " or (" << *def << ")";
+ str << "(";
+ e->show(symbols, str);
+ str << ")." << showAttrPath(symbols, attrPath);
+ if (def) {
+ str << " or (";
+ def->show(symbols, str);
+ str << ")";
+ }
}
-void ExprOpHasAttr::show(std::ostream & str) const
+void ExprOpHasAttr::show(const SymbolTable & symbols, std::ostream & str) const
{
- str << "((" << *e << ") ? " << showAttrPath(attrPath) << ")";
+ str << "((";
+ e->show(symbols, str);
+ str << ") ? " << showAttrPath(symbols, attrPath) << ")";
}
-void ExprAttrs::show(std::ostream & str) const
+void ExprAttrs::show(const SymbolTable & symbols, std::ostream & str) const
{
if (recursive) str << "rec ";
str << "{ ";
typedef const decltype(attrs)::value_type * Attr;
std::vector sorted;
for (auto & i : attrs) sorted.push_back(&i);
- std::sort(sorted.begin(), sorted.end(), [](Attr a, Attr b) {
- return (const std::string &) a->first < (const std::string &) b->first;
- });
+ std::sort(sorted.begin(), sorted.end(), [&](Attr a, Attr b) {
+ std::string_view sa = symbols[a->first], sb = symbols[b->first];
+ return sa < sb;
+ });
for (auto & i : sorted) {
if (i->second.inherited)
- str << "inherit " << i->first << " " << "; ";
- else
- str << i->first << " = " << *i->second.e << "; ";
+ str << "inherit " << symbols[i->first] << " " << "; ";
+ else {
+ str << symbols[i->first] << " = ";
+ i->second.e->show(symbols, str);
+ str << "; ";
+ }
+ }
+ for (auto & i : dynamicAttrs) {
+ str << "\"${";
+ i.nameExpr->show(symbols, str);
+ str << "}\" = ";
+ i.valueExpr->show(symbols, str);
+ str << "; ";
}
- for (auto & i : dynamicAttrs)
- str << "\"${" << *i.nameExpr << "}\" = " << *i.valueExpr << "; ";
str << "}";
}
-void ExprList::show(std::ostream & str) const
+void ExprList::show(const SymbolTable & symbols, std::ostream & str) const
{
str << "[ ";
- for (auto & i : elems)
- str << "(" << *i << ") ";
+ for (auto & i : elems) {
+ str << "(";
+ i->show(symbols, str);
+ str << ") ";
+ }
str << "]";
}
-void ExprLambda::show(std::ostream & str) const
+void ExprLambda::show(const SymbolTable & symbols, std::ostream & str) const
{
str << "(";
if (hasFormals()) {
@@ -136,74 +149,100 @@ void ExprLambda::show(std::ostream & str) const
bool first = true;
for (auto & i : formals->formals) {
if (first) first = false; else str << ", ";
- str << i.name;
- if (i.def) str << " ? " << *i.def;
+ str << symbols[i.name];
+ if (i.def) {
+ str << " ? ";
+ i.def->show(symbols, str);
+ }
}
if (formals->ellipsis) {
if (!first) str << ", ";
str << "...";
}
str << " }";
- if (!arg.empty()) str << " @ ";
+ if (arg) str << " @ ";
}
- if (!arg.empty()) str << arg;
- str << ": " << *body << ")";
+ if (arg) str << symbols[arg];
+ str << ": ";
+ body->show(symbols, str);
+ str << ")";
}
-void ExprCall::show(std::ostream & str) const
+void ExprCall::show(const SymbolTable & symbols, std::ostream & str) const
{
- str << '(' << *fun;
+ str << '(';
+ fun->show(symbols, str);
for (auto e : args) {
str << ' ';
- str << *e;
+ e->show(symbols, str);
}
str << ')';
}
-void ExprLet::show(std::ostream & str) const
+void ExprLet::show(const SymbolTable & symbols, std::ostream & str) const
{
str << "(let ";
for (auto & i : attrs->attrs)
if (i.second.inherited) {
- str << "inherit " << i.first << "; ";
+ str << "inherit " << symbols[i.first] << "; ";
}
- else
- str << i.first << " = " << *i.second.e << "; ";
- str << "in " << *body << ")";
+ else {
+ str << symbols[i.first] << " = ";
+ i.second.e->show(symbols, str);
+ str << "; ";
+ }
+ str << "in ";
+ body->show(symbols, str);
+ str << ")";
}
-void ExprWith::show(std::ostream & str) const
+void ExprWith::show(const SymbolTable & symbols, std::ostream & str) const
{
- str << "(with " << *attrs << "; " << *body << ")";
+ str << "(with ";
+ attrs->show(symbols, str);
+ str << "; ";
+ body->show(symbols, str);
+ str << ")";
}
-void ExprIf::show(std::ostream & str) const
+void ExprIf::show(const SymbolTable & symbols, std::ostream & str) const
{
- str << "(if " << *cond << " then " << *then << " else " << *else_ << ")";
+ str << "(if ";
+ cond->show(symbols, str);
+ str << " then ";
+ then->show(symbols, str);
+ str << " else ";
+ else_->show(symbols, str);
+ str << ")";
}
-void ExprAssert::show(std::ostream & str) const
+void ExprAssert::show(const SymbolTable & symbols, std::ostream & str) const
{
- str << "assert " << *cond << "; " << *body;
+ str << "assert ";
+ cond->show(symbols, str);
+ str << "; ";
+ body->show(symbols, str);
}
-void ExprOpNot::show(std::ostream & str) const
+void ExprOpNot::show(const SymbolTable & symbols, std::ostream & str) const
{
- str << "(! " << *e << ")";
+ str << "(! ";
+ e->show(symbols, str);
+ str << ")";
}
-void ExprConcatStrings::show(std::ostream & str) const
+void ExprConcatStrings::show(const SymbolTable & symbols, std::ostream & str) const
{
bool first = true;
str << "(";
for (auto & i : *es) {
if (first) first = false; else str << " + ";
- str << *i.second;
+ i.second->show(symbols, str);
}
str << ")";
}
-void ExprPos::show(std::ostream & str) const
+void ExprPos::show(const SymbolTable & symbols, std::ostream & str) const
{
str << "__curPos";
}
@@ -234,48 +273,49 @@ std::ostream & operator << (std::ostream & str, const Pos & pos)
}
-std::string showAttrPath(const AttrPath & attrPath)
+std::string showAttrPath(const SymbolTable & symbols, const AttrPath & attrPath)
{
std::ostringstream out;
bool first = true;
for (auto & i : attrPath) {
if (!first) out << '.'; else first = false;
- if (i.symbol.set())
- out << i.symbol;
- else
- out << "\"${" << *i.expr << "}\"";
+ if (i.symbol)
+ out << symbols[i.symbol];
+ else {
+ out << "\"${";
+ i.expr->show(symbols, out);
+ out << "}\"";
+ }
}
return out.str();
}
-Pos noPos;
-
/* Computing levels/displacements for variables. */
-void Expr::bindVars(const StaticEnv & env)
+void Expr::bindVars(const EvalState & es, const StaticEnv & env)
{
abort();
}
-void ExprInt::bindVars(const StaticEnv & env)
+void ExprInt::bindVars(const EvalState & es, const StaticEnv & env)
{
}
-void ExprFloat::bindVars(const StaticEnv & env)
+void ExprFloat::bindVars(const EvalState & es, const StaticEnv & env)
{
}
-void ExprString::bindVars(const StaticEnv & env)
+void ExprString::bindVars(const EvalState & es, const StaticEnv & env)
{
}
-void ExprPath::bindVars(const StaticEnv & env)
+void ExprPath::bindVars(const EvalState & es, const StaticEnv & env)
{
}
-void ExprVar::bindVars(const StaticEnv & env)
+void ExprVar::bindVars(const EvalState & es, const StaticEnv & env)
{
/* Check whether the variable appears in the environment. If so,
set its level and displacement. */
@@ -301,31 +341,31 @@ void ExprVar::bindVars(const StaticEnv & env)
"undefined variable" error now. */
if (withLevel == -1)
throw UndefinedVarError({
- .msg = hintfmt("undefined variable '%1%'", name),
- .errPos = pos
+ .msg = hintfmt("undefined variable '%1%'", es.symbols[name]),
+ .errPos = es.positions[pos]
});
fromWith = true;
this->level = withLevel;
}
-void ExprSelect::bindVars(const StaticEnv & env)
+void ExprSelect::bindVars(const EvalState & es, const StaticEnv & env)
{
- e->bindVars(env);
- if (def) def->bindVars(env);
+ e->bindVars(es, env);
+ if (def) def->bindVars(es, env);
for (auto & i : attrPath)
- if (!i.symbol.set())
- i.expr->bindVars(env);
+ if (!i.symbol)
+ i.expr->bindVars(es, env);
}
-void ExprOpHasAttr::bindVars(const StaticEnv & env)
+void ExprOpHasAttr::bindVars(const EvalState & es, const StaticEnv & env)
{
- e->bindVars(env);
+ e->bindVars(es, env);
for (auto & i : attrPath)
- if (!i.symbol.set())
- i.expr->bindVars(env);
+ if (!i.symbol)
+ i.expr->bindVars(es, env);
}
-void ExprAttrs::bindVars(const StaticEnv & env)
+void ExprAttrs::bindVars(const EvalState & es, const StaticEnv & env)
{
const StaticEnv * dynamicEnv = &env;
StaticEnv newEnv(false, &env, recursive ? attrs.size() : 0);
@@ -340,35 +380,35 @@ void ExprAttrs::bindVars(const StaticEnv & env)
// No need to sort newEnv since attrs is in sorted order.
for (auto & i : attrs)
- i.second.e->bindVars(i.second.inherited ? env : newEnv);
+ i.second.e->bindVars(es, i.second.inherited ? env : newEnv);
}
else
for (auto & i : attrs)
- i.second.e->bindVars(env);
+ i.second.e->bindVars(es, env);
for (auto & i : dynamicAttrs) {
- i.nameExpr->bindVars(*dynamicEnv);
- i.valueExpr->bindVars(*dynamicEnv);
+ i.nameExpr->bindVars(es, *dynamicEnv);
+ i.valueExpr->bindVars(es, *dynamicEnv);
}
}
-void ExprList::bindVars(const StaticEnv & env)
+void ExprList::bindVars(const EvalState & es, const StaticEnv & env)
{
for (auto & i : elems)
- i->bindVars(env);
+ i->bindVars(es, env);
}
-void ExprLambda::bindVars(const StaticEnv & env)
+void ExprLambda::bindVars(const EvalState & es, const StaticEnv & env)
{
StaticEnv newEnv(
false, &env,
(hasFormals() ? formals->formals.size() : 0) +
- (arg.empty() ? 0 : 1));
+ (!arg ? 0 : 1));
Displacement displ = 0;
- if (!arg.empty()) newEnv.vars.emplace_back(arg, displ++);
+ if (arg) newEnv.vars.emplace_back(arg, displ++);
if (hasFormals()) {
for (auto & i : formals->formals)
@@ -377,20 +417,20 @@ void ExprLambda::bindVars(const StaticEnv & env)
newEnv.sort();
for (auto & i : formals->formals)
- if (i.def) i.def->bindVars(newEnv);
+ if (i.def) i.def->bindVars(es, newEnv);
}
- body->bindVars(newEnv);
+ body->bindVars(es, newEnv);
}
-void ExprCall::bindVars(const StaticEnv & env)
+void ExprCall::bindVars(const EvalState & es, const StaticEnv & env)
{
- fun->bindVars(env);
+ fun->bindVars(es, env);
for (auto e : args)
- e->bindVars(env);
+ e->bindVars(es, env);
}
-void ExprLet::bindVars(const StaticEnv & env)
+void ExprLet::bindVars(const EvalState & es, const StaticEnv & env)
{
StaticEnv newEnv(false, &env, attrs->attrs.size());
@@ -401,12 +441,12 @@ void ExprLet::bindVars(const StaticEnv & env)
// No need to sort newEnv since attrs->attrs is in sorted order.
for (auto & i : attrs->attrs)
- i.second.e->bindVars(i.second.inherited ? env : newEnv);
+ i.second.e->bindVars(es, i.second.inherited ? env : newEnv);
- body->bindVars(newEnv);
+ body->bindVars(es, newEnv);
}
-void ExprWith::bindVars(const StaticEnv & env)
+void ExprWith::bindVars(const EvalState & es, const StaticEnv & env)
{
/* Does this `with' have an enclosing `with'? If so, record its
level so that `lookupVar' can look up variables in the previous
@@ -420,57 +460,60 @@ void ExprWith::bindVars(const StaticEnv & env)
break;
}
- attrs->bindVars(env);
+ attrs->bindVars(es, env);
StaticEnv newEnv(true, &env);
- body->bindVars(newEnv);
+ body->bindVars(es, newEnv);
}
-void ExprIf::bindVars(const StaticEnv & env)
+void ExprIf::bindVars(const EvalState & es, const StaticEnv & env)
{
- cond->bindVars(env);
- then->bindVars(env);
- else_->bindVars(env);
+ cond->bindVars(es, env);
+ then->bindVars(es, env);
+ else_->bindVars(es, env);
}
-void ExprAssert::bindVars(const StaticEnv & env)
+void ExprAssert::bindVars(const EvalState & es, const StaticEnv & env)
{
- cond->bindVars(env);
- body->bindVars(env);
+ cond->bindVars(es, env);
+ body->bindVars(es, env);
}
-void ExprOpNot::bindVars(const StaticEnv & env)
+void ExprOpNot::bindVars(const EvalState & es, const StaticEnv & env)
{
- e->bindVars(env);
+ e->bindVars(es, env);
}
-void ExprConcatStrings::bindVars(const StaticEnv & env)
+void ExprConcatStrings::bindVars(const EvalState & es, const StaticEnv & env)
{
- for (auto & i : *es)
- i.second->bindVars(env);
+ for (auto & i : *this->es)
+ i.second->bindVars(es, env);
}
-void ExprPos::bindVars(const StaticEnv & env)
+void ExprPos::bindVars(const EvalState & es, const StaticEnv & env)
{
}
/* Storing function names. */
-void Expr::setName(Symbol & name)
+void Expr::setName(Symbol name)
{
}
-void ExprLambda::setName(Symbol & name)
+void ExprLambda::setName(Symbol name)
{
this->name = name;
body->setName(name);
}
-std::string ExprLambda::showNamePos() const
+std::string ExprLambda::showNamePos(const EvalState & state) const
{
- return fmt("%1% at %2%", name.set() ? "'" + (std::string) name + "'" : "anonymous function", pos);
+ std::string id(name
+ ? concatStrings("'", state.symbols[name], "'")
+ : "anonymous function");
+ return fmt("%1% at %2%", id, state.positions[pos]);
}
@@ -480,8 +523,7 @@ std::string ExprLambda::showNamePos() const
size_t SymbolTable::totalSize() const
{
size_t n = 0;
- for (auto & i : store)
- n += i.size();
+ dump([&] (const std::string & s) { n += s.size(); });
return n;
}
diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh
index 2eafa1a53..68b83556a 100644
--- a/src/libexpr/nixexpr.hh
+++ b/src/libexpr/nixexpr.hh
@@ -1,8 +1,12 @@
#pragma once
+#include ]