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/advanced-topics/distributed-builds.md b/doc/manual/src/advanced-topics/distributed-builds.md
index c4c60db15..b0d7fbf1a 100644
--- a/doc/manual/src/advanced-topics/distributed-builds.md
+++ b/doc/manual/src/advanced-topics/distributed-builds.md
@@ -110,7 +110,7 @@ default, set it to `-`.
7. A comma-separated list of *mandatory features*. A machine will only
be used to build a derivation if all of the machine’s mandatory
features appear in the derivation’s `requiredSystemFeatures`
- attribute..
+ attribute.
8. The (base64-encoded) public host key of the remote machine. If omitted, SSH
will use its regular known-hosts file. Specifically, the field is calculated
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 52e3b6240..e57054861 100644
--- a/doc/manual/src/release-notes/rl-next.md
+++ b/doc/manual/src/release-notes/rl-next.md
@@ -1,4 +1,14 @@
# Release X.Y (202?-??-??)
+* `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`.
+
* Nix can now be built with LTO by passing `--enable-lto` to `configure`.
- LTO is currently only supported when building with GCC.
+ LTO is currently only supported when building with GCC.
\ No newline at end of file
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 25655204d..24d894898 100644
--- a/misc/systemd/nix-daemon.service.in
+++ b/misc/systemd/nix-daemon.service.in
@@ -1,7 +1,9 @@
[Unit]
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/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 b7623d4ba..c81f76a22 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,12 +139,14 @@ MixFlakeOptions::MixFlakeOptions()
});
}
-SourceExprCommand::SourceExprCommand()
+SourceExprCommand::SourceExprCommand(bool supportReadOnlyMode)
{
addFlag({
.longName = "file",
.shortName = 'f',
- .description = "Interpret installables as attribute paths relative to the Nix expression stored in *file*.",
+ .description =
+ "Interpret installables as attribute paths relative to the Nix expression stored in *file*. "
+ "If *file* is the character -, then a Nix expression will be read from standard input.",
.category = installablesCategory,
.labels = {"file"},
.handler = {&file},
@@ -155,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()
@@ -180,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(
@@ -208,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);
}
}
}
@@ -242,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)));
@@ -257,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);
@@ -332,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);
@@ -448,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);
@@ -564,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("outputName")->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()
@@ -610,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,
@@ -646,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;
@@ -685,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");
@@ -695,7 +713,10 @@ std::vector> SourceExprCommand::parseInstallables(
auto state = getEvalState();
auto vFile = state->allocValue();
- if (file)
+ if (file == "-") {
+ auto e = state->parseStdin();
+ state->eval(e, *vFile);
+ } else if (file)
state->evalFile(lookupFileArg(*state, *file), *vFile);
else {
auto e = state->parseExprFromString(*expr, absPath("."));
@@ -751,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,
@@ -810,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(
@@ -930,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);
@@ -940,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 32deecfae..f63caeb10 100644
--- a/src/libexpr/attr-path.cc
+++ b/src/libexpr/attr-path.cc
@@ -36,18 +36,18 @@ std::vector parseAttrPath(EvalState & state, std::string_view s)
{
std::vector res;
for (auto & a : parseAttrPath(s))
- res.push_back(state.symbols.create(a));
+ res.emplace_back(a);
return res;
}
-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..1d17ef7e4 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, const SymbolIdx & 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(const SymbolIdx & 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..3bf23eeb2 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
{
- Symbol name;
+ /* 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. */
+ SymbolIdx 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(SymbolIdx 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(const SymbolIdx & 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(const SymbolIdx & 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(SymbolIdx 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(const SymbolIdx & 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 188223957..0d2160efd 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,9 @@ struct AttrDb
std::unique_ptr> _state;
- AttrDb(const Hash & fingerprint)
- : _state(std::make_unique>())
+ AttrDb(const Store & cfg, const Hash & fingerprint)
+ : cfg(cfg)
+ , _state(std::make_unique>())
{
auto state(_state->lock());
@@ -250,14 +253,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(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 +277,10 @@ struct AttrDb
}
};
-static std::shared_ptr makeAttrDb(const Hash & fingerprint)
+static std::shared_ptr makeAttrDb(const Store & cfg, const Hash & fingerprint)
{
try {
- return std::make_shared(fingerprint);
+ return std::make_shared(cfg, fingerprint);
} catch (SQLiteError &) {
ignoreException();
return nullptr;
@@ -288,7 +291,7 @@ EvalCache::EvalCache(
std::optional> useCache,
EvalState & state,
RootLoader rootLoader)
- : db(useCache ? makeAttrDb(*useCache) : nullptr)
+ : db(useCache ? makeAttrDb(*state.store, *useCache) : nullptr)
, state(state)
, rootLoader(rootLoader)
{
@@ -303,9 +306,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(
@@ -322,7 +325,7 @@ AttrCursor::AttrCursor(
AttrKey AttrCursor::getKey()
{
if (!parent)
- return {0, root->state.sEpsilon};
+ return {0, {""}};
if (!parent->first->cachedValue) {
parent->first->cachedValue = root->db->getAttr(
parent->first->getKey(), root->state.symbols);
@@ -337,7 +340,7 @@ Value & AttrCursor::getValue()
if (parent) {
auto & vParent = parent->first->getValue();
root->state.forceAttrs(vParent, noPos);
- auto attr = vParent.attrs->get(parent->second);
+ auto attr = vParent.attrs->get(root->state.symbols.create(parent->second));
if (!attr)
throw Error("attribute '%s' is unexpectedly missing", getAttrPathStr());
_value = allocRootValue(attr->value);
@@ -416,7 +419,7 @@ Suggestions AttrCursor::getSuggestionsForAttr(Symbol name)
return Suggestions::bestMatches(strAttrNames, name);
}
-std::shared_ptr AttrCursor::maybeGetAttr(Symbol name, bool forceErrors)
+std::shared_ptr AttrCursor::maybeGetAttr(std::string_view name, bool forceErrors)
{
if (root->db) {
if (!cachedValue)
@@ -458,10 +461,10 @@ std::shared_ptr AttrCursor::maybeGetAttr(Symbol name, bool forceErro
for (auto & attr : *v.attrs) {
if (root->db)
- root->db->setPlaceholder({cachedValue->first, attr.name});
+ root->db->setPlaceholder({cachedValue->first, root->state.symbols[attr.name]});
}
- auto attr = v.attrs->get(name);
+ auto attr = v.attrs->get(root->state.symbols.create(name));
if (!attr) {
if (root->db) {
@@ -483,12 +486,7 @@ std::shared_ptr AttrCursor::maybeGetAttr(Symbol name, bool forceErro
root, std::make_pair(shared_from_this(), name), attr->value, std::move(cachedValue2));
}
-std::shared_ptr AttrCursor::maybeGetAttr(std::string_view name)
-{
- return maybeGetAttr(root->state.symbols.create(name));
-}
-
-ref AttrCursor::getAttr(Symbol name, bool forceErrors)
+ref AttrCursor::getAttr(std::string_view name, bool forceErrors)
{
auto p = maybeGetAttr(name, forceErrors);
if (!p)
@@ -496,11 +494,6 @@ ref AttrCursor::getAttr(Symbol name, bool forceErrors)
return ref(p);
}
-ref AttrCursor::getAttr(std::string_view name)
-{
- return getAttr(root->state.symbols.create(name));
-}
-
OrSuggestions][> AttrCursor::findAlongAttrPath(const std::vector & attrPath, bool force)
{
auto res = shared_from_this();
@@ -546,7 +539,7 @@ string_t AttrCursor::getStringWithContext()
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 +556,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
@@ -613,7 +606,7 @@ std::vector AttrCursor::getAttrs()
std::vector attrs;
for (auto & attr : *getValue().attrs)
- attrs.push_back(attr.name);
+ attrs.push_back(root->state.symbols[attr.name]);
std::sort(attrs.begin(), attrs.end(), [](const Symbol & a, const Symbol & b) {
return (const std::string &) a < (const std::string &) b;
});
@@ -632,7 +625,7 @@ bool AttrCursor::isDerivation()
StorePath AttrCursor::forceDerivation()
{
- auto aDrvPath = getAttr(root->state.sDrvPath, true);
+ auto aDrvPath = getAttr("drvPath", true);
auto drvPath = root->state.store->parseStorePath(aDrvPath->getString());
if (!root->state.store->isValidPath(drvPath) && !settings.readOnlyMode) {
/* The eval cache contains 'drvPath', but the actual path has
diff --git a/src/libexpr/eval-cache.hh b/src/libexpr/eval-cache.hh
index 40f1d4ffc..f4481c72a 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,
@@ -96,14 +96,12 @@ public:
Suggestions getSuggestionsForAttr(Symbol name);
- std::shared_ptr maybeGetAttr(Symbol name, bool forceErrors = false);
+ std::shared_ptr maybeGetAttr(std::string_view name, bool forceErrors = false);
- std::shared_ptr maybeGetAttr(std::string_view name);
-
- ref getAttr(Symbol name, bool forceErrors = false);
-
- ref getAttr(std::string_view name);
+ ref getAttr(std::string_view name, bool forceErrors = false);
+ /* 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 aef1f6351..7f01d08e3 100644
--- a/src/libexpr/eval-inline.hh
+++ b/src/libexpr/eval-inline.hh
@@ -2,29 +2,85 @@
#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))
+/* Note: Various places expect the allocated memory to be zeroed. */
+[[gnu::always_inline]]
+inline void * allocBytes(size_t n)
{
- throw TypeError({
- .msg = hintfmt(s, showType(v)),
- .errPos = pos
- });
+ void * p;
+#if HAVE_BOEHMGC
+ p = GC_MALLOC(n);
+#else
+ p = calloc(n, 1);
+#endif
+ if (!p) throw std::bad_alloc();
+ return p;
}
-void EvalState::forceValue(Value & v, const Pos & pos)
+[[gnu::always_inline]]
+Value * EvalState::allocValue()
+{
+#if HAVE_BOEHMGC
+ /* We use the boehm batch allocator to speed up allocations of Values (of which there are many).
+ GC_malloc_many returns a linked list of objects of the given size, where the first word
+ of each object is also the pointer to the next object in the list. This also means that we
+ have to explicitly clear the first word of every object we take. */
+ if (!*valueAllocCache) {
+ *valueAllocCache = GC_malloc_many(sizeof(Value));
+ if (!*valueAllocCache) throw std::bad_alloc();
+ }
+
+ /* GC_NEXT is a convenience macro for accessing the first word of an object.
+ Take the first list item, advance the list to the next item, and clear the next pointer. */
+ void * p = *valueAllocCache;
+ *valueAllocCache = GC_NEXT(p);
+ GC_NEXT(p) = nullptr;
+#else
+ void * p = allocBytes(sizeof(Value));
+#endif
+
+ nrValues++;
+ return (Value *) p;
+}
+
+
+[[gnu::always_inline]]
+Env & EvalState::allocEnv(size_t size)
+{
+ nrEnvs++;
+ nrValuesInEnvs += size;
+
+ Env * env;
+
+#if HAVE_BOEHMGC
+ if (size == 1) {
+ /* see allocValue for explanations. */
+ if (!*env1AllocCache) {
+ *env1AllocCache = GC_malloc_many(sizeof(Env) + sizeof(Value *));
+ if (!*env1AllocCache) throw std::bad_alloc();
+ }
+
+ void * p = *env1AllocCache;
+ *env1AllocCache = GC_NEXT(p);
+ GC_NEXT(p) = nullptr;
+ env = (Env *) p;
+ } else
+#endif
+ env = (Env *) allocBytes(sizeof(Env) + size * sizeof(Value *));
+
+ env->type = Env::Plain;
+
+ /* We assume that env->values has been cleared by the allocator; maybeThunk() and lookupVar fromWith expect this. */
+
+ return *env;
+}
+
+
+[[gnu::always_inline]]
+void EvalState::forceValue(Value & v, const PosIdx pos)
{
forceValue(v, [&]() { return pos; });
}
@@ -52,13 +108,15 @@ void EvalState::forceValue(Value & v, Callable getPos)
}
-inline void EvalState::forceAttrs(Value & v, const Pos & pos)
+[[gnu::always_inline]]
+inline void EvalState::forceAttrs(Value & v, const PosIdx pos)
{
forceAttrs(v, [&]() { return pos; });
}
template
+[[gnu::always_inline]]
inline void EvalState::forceAttrs(Value & v, Callable getPos)
{
forceValue(v, getPos);
@@ -67,25 +125,13 @@ inline void EvalState::forceAttrs(Value & v, Callable getPos)
}
-inline void EvalState::forceList(Value & v, const Pos & pos)
+[[gnu::always_inline]]
+inline void EvalState::forceList(Value & v, const PosIdx pos)
{
forceValue(v, pos);
if (!v.isList())
throwTypeError(pos, "value is %1% while a list was expected", v);
}
-/* Note: Various places expect the allocated memory to be zeroed. */
-inline void * allocBytes(size_t n)
-{
- void * p;
-#if HAVE_BOEHMGC
- p = GC_MALLOC(n);
-#else
- p = calloc(n, 1);
-#endif
- if (!p) throw std::bad_alloc();
- return p;
-}
-
}
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index 5bf161cc0..8fc144a84 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;
@@ -300,9 +308,9 @@ static BoehmGCStackAllocator boehmGCStackAllocator;
#endif
-static Symbol getName(const AttrName & name, EvalState & state, Env & env)
+static SymbolIdx 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"))
@@ -449,8 +458,10 @@ EvalState::EvalState(
, regexCache(makeRegexCache())
#if HAVE_BOEHMGC
, valueAllocCache(std::allocate_shared(traceable_allocator(), nullptr))
+ , env1AllocCache(std::allocate_shared(traceable_allocator(), nullptr))
#else
, valueAllocCache(std::make_shared(nullptr))
+ , env1AllocCache(std::make_shared(nullptr))
#endif
, baseEnv(allocEnv(128))
, staticBaseEnv(false, 0)
@@ -499,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)
@@ -647,20 +641,20 @@ Value * EvalState::addPrimOp(const std::string & name,
size_t arity, PrimOpFun primOp)
{
auto name2 = name.substr(0, 2) == "__" ? name.substr(2) : name;
- Symbol sym = symbols.create(name2);
+ auto sym = symbols.create(name2);
/* Hack to make constants lazy: turn them into a application of
the primop to a dummy value. */
if (arity == 0) {
auto vPrimOp = allocValue();
- vPrimOp->mkPrimOp(new PrimOp { .fun = primOp, .arity = 1, .name = sym });
+ vPrimOp->mkPrimOp(new PrimOp { .fun = primOp, .arity = 1, .name = name2 });
Value v;
v.mkApp(vPrimOp, vPrimOp);
return addConstant(name, v);
}
Value * v = allocValue();
- v->mkPrimOp(new PrimOp { .fun = primOp, .arity = arity, .name = sym });
+ v->mkPrimOp(new PrimOp { .fun = primOp, .arity = arity, .name = name2 });
staticBaseEnv.vars.emplace_back(symbols.create(name), baseEnvDispl);
baseEnv.values[baseEnvDispl++] = v;
baseEnv.values[0]->attrs->push_back(Attr(sym, v));
@@ -675,21 +669,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;
}
@@ -706,7 +700,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,
@@ -722,94 +716,133 @@ std::optional EvalState::getDoc(Value & v)
evaluator. So here are some helper functions for throwing
exceptions. */
-LocalNoInlineNoReturn(void throwEvalError(const char * s, const std::string & s2))
+void EvalState::throwEvalError(const PosIdx pos, const char * s) const
+{
+ throw EvalError({
+ .msg = hintfmt(s),
+ .errPos = positions[pos]
+ });
+}
+
+void EvalState::throwTypeError(const PosIdx pos, const char * s, const Value & v) const
+{
+ throw TypeError({
+ .msg = hintfmt(s, showType(v)),
+ .errPos = positions[pos]
+ });
+}
+
+void EvalState::throwEvalError(const char * s, const std::string & s2) const
{
throw EvalError(s, s2);
}
-LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const std::string & s2))
+void EvalState::throwEvalError(const PosIdx pos, const Suggestions & suggestions, const char * s,
+ const std::string & s2) const
{
- throw EvalError({
+ throw EvalError(ErrorInfo {
.msg = hintfmt(s, s2),
- .errPos = pos
+ .errPos = positions[pos],
+ .suggestions = suggestions,
});
}
-LocalNoInlineNoReturn(void throwEvalError(const char * s, const std::string & s2, const std::string & s3))
+void EvalState::throwEvalError(const PosIdx pos, const char * s, const std::string & s2) const
+{
+ throw EvalError(ErrorInfo {
+ .msg = hintfmt(s, s2),
+ .errPos = positions[pos]
+ });
+}
+
+void EvalState::throwEvalError(const char * s, const std::string & s2, const std::string & s3) const
{
throw EvalError(s, s2, s3);
}
-LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const std::string & s2, const std::string & s3))
+void EvalState::throwEvalError(const PosIdx pos, const char * s, const std::string & s2,
+ const std::string & s3) const
{
throw EvalError({
.msg = hintfmt(s, s2, s3),
- .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 p1, const char * s, const SymbolIdx sym, const PosIdx p2) const
{
// p1 is where the error occurred; p2 is a position mentioned in the message.
throw EvalError({
- .msg = hintfmt(s, sym, p2),
- .errPos = p1
+ .msg = hintfmt(s, symbols[sym], positions[p2]),
+ .errPos = positions[p1]
});
}
-LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s))
+void EvalState::throwTypeError(const PosIdx pos, const char * s) const
{
throw TypeError({
.msg = hintfmt(s),
- .errPos = pos
+ .errPos = positions[pos]
});
}
-LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const ExprLambda & fun, const Symbol & s2))
+void EvalState::throwTypeError(const PosIdx pos, const char * s, const ExprLambda & fun,
+ const SymbolIdx s2) const
{
throw TypeError({
- .msg = hintfmt(s, fun.showNamePos(), s2),
- .errPos = pos
+ .msg = hintfmt(s, fun.showNamePos(*this), symbols[s2]),
+ .errPos = positions[pos]
});
}
-LocalNoInlineNoReturn(void throwTypeError(const char * s, const Value & v))
+void EvalState::throwTypeError(const PosIdx pos, const Suggestions & suggestions, const char * s,
+ const ExprLambda & fun, const SymbolIdx s2) const
+{
+ throw TypeError(ErrorInfo {
+ .msg = hintfmt(s, fun.showNamePos(*this), symbols[s2]),
+ .errPos = positions[pos],
+ .suggestions = suggestions,
+ });
+}
+
+
+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::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);
}
@@ -866,52 +899,16 @@ 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) ;
}
}
-Value * EvalState::allocValue()
-{
- /* We use the boehm batch allocator to speed up allocations of Values (of which there are many).
- GC_malloc_many returns a linked list of objects of the given size, where the first word
- of each object is also the pointer to the next object in the list. This also means that we
- have to explicitly clear the first word of every object we take. */
- if (!*valueAllocCache) {
- *valueAllocCache = GC_malloc_many(sizeof(Value));
- if (!*valueAllocCache) throw std::bad_alloc();
- }
-
- /* GC_NEXT is a convenience macro for accessing the first word of an object.
- Take the first list item, advance the list to the next item, and clear the next pointer. */
- void * p = *valueAllocCache;
- GC_PTR_STORE_AND_DIRTY(&*valueAllocCache, GC_NEXT(p));
- GC_NEXT(p) = nullptr;
-
- nrValues++;
- auto v = (Value *) p;
- return v;
-}
-
-
-Env & EvalState::allocEnv(size_t size)
-{
- nrEnvs++;
- nrValuesInEnvs += size;
- Env * env = (Env *) allocBytes(sizeof(Env) + size * sizeof(Value *));
- env->type = Env::Plain;
-
- /* We assume that env->values has been cleared by the allocator; maybeThunk() and lookupVar fromWith expect this. */
-
- return *env;
-}
-
-
void EvalState::mkList(Value & v, size_t size)
{
v.mkList(size);
@@ -936,13 +933,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();
@@ -1075,7 +1073,7 @@ inline bool EvalState::evalBool(Env & env, Expr * e)
}
-inline bool EvalState::evalBool(Env & env, Expr * e, const Pos & pos)
+inline bool EvalState::evalBool(Env & env, Expr * e, const PosIdx pos)
{
Value v;
e->eval(*this, env, v);
@@ -1149,7 +1147,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
@@ -1181,7 +1179,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) {
@@ -1191,18 +1189,18 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
if (nameVal.type() == nNull)
continue;
state.forceStringNoCtx(nameVal);
- 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;
}
@@ -1247,10 +1245,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();
@@ -1260,7 +1260,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);
@@ -1270,7 +1270,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 ||
@@ -1281,19 +1281,27 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
}
} else {
state.forceAttrs(*vAttrs, pos);
- if ((j = vAttrs->attrs->find(name)) == vAttrs->attrs->end())
- throwEvalError(pos, "attribute '%1%' missing", name);
+ if ((j = vAttrs->attrs->find(name)) == vAttrs->attrs->end()) {
+ std::set allAttrNames;
+ for (auto & attr : *vAttrs->attrs)
+ allAttrNames.insert(state.symbols[attr.name]);
+ state.throwEvalError(
+ pos,
+ 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;
}
@@ -1312,7 +1320,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())
{
@@ -1333,9 +1341,11 @@ void ExprLambda::eval(EvalState & state, Env & env, Value & v)
}
-void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & vRes, const Pos & pos)
+void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & vRes, const PosIdx pos)
{
- auto trace = evalSettings.traceFunctionCalls ? std::make_unique(pos) : nullptr;
+ auto trace = evalSettings.traceFunctionCalls
+ ? std::make_unique(positions[pos])
+ : nullptr;
forceValue(fun, pos);
@@ -1360,7 +1370,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;
@@ -1373,7 +1383,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
else {
forceAttrs(*args[0], pos);
- if (!lambda.arg.empty())
+ if (lambda.arg)
env2.values[displ++] = args[0];
/* For each formal argument, get the actual argument. If
@@ -1398,8 +1408,17 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
/* Nope, so show the first unexpected argument to the
user. */
for (auto & i : *args[0]->attrs)
- if (!lambda.formals->has(i.name))
- throwTypeError(pos, "%1% called with unexpected argument '%2%'", lambda, i.name);
+ if (!lambda.formals->has(i.name)) {
+ std::set formalNames;
+ for (auto & formal : lambda.formals->formals)
+ formalNames.insert(symbols[formal.name]);
+ throwTypeError(
+ pos,
+ Suggestions::bestMatches(formalNames, symbols[i.name]),
+ "%1% called with unexpected argument '%2%'",
+ lambda,
+ i.name);
+ }
abort(); // can't happen
}
}
@@ -1413,8 +1432,8 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
} catch (Error & e) {
if (loggerSettings.showTrace.get()) {
addErrorTrace(e, lambda.pos, "while evaluating %s",
- (lambda.name.set()
- ? "'" + (const std::string &) lambda.name + "'"
+ (lambda.name
+ ? concatStrings("'", symbols[lambda.name], "'")
: "anonymous lambda"));
addErrorTrace(e, pos, "from call site%s", "");
}
@@ -1563,7 +1582,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]);
}
}
@@ -1595,8 +1614,8 @@ void ExprAssert::eval(EvalState & state, Env & env, Value & v)
{
if (!state.evalBool(env, cond, pos)) {
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);
}
@@ -1689,7 +1708,7 @@ void ExprOpConcatLists::eval(EvalState & state, Env & env, Value & v)
}
-void EvalState::concatLists(Value & v, size_t nrLists, Value * * lists, const Pos & pos)
+void EvalState::concatLists(Value & v, size_t nrLists, Value * * lists, const PosIdx pos)
{
nrListConcats++;
@@ -1773,14 +1792,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", showType(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", showType(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
@@ -1800,7 +1819,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);
@@ -1809,7 +1828,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);
}
@@ -1829,7 +1848,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;
}
}
@@ -1844,7 +1863,7 @@ void EvalState::forceValueDeep(Value & v)
}
-NixInt EvalState::forceInt(Value & v, const Pos & pos)
+NixInt EvalState::forceInt(Value & v, const PosIdx pos)
{
forceValue(v, pos);
if (v.type() != nInt)
@@ -1853,7 +1872,7 @@ NixInt EvalState::forceInt(Value & v, const Pos & pos)
}
-NixFloat EvalState::forceFloat(Value & v, const Pos & pos)
+NixFloat EvalState::forceFloat(Value & v, const PosIdx pos)
{
forceValue(v, pos);
if (v.type() == nInt)
@@ -1864,7 +1883,7 @@ NixFloat EvalState::forceFloat(Value & v, const Pos & pos)
}
-bool EvalState::forceBool(Value & v, const Pos & pos)
+bool EvalState::forceBool(Value & v, const PosIdx pos)
{
forceValue(v, pos);
if (v.type() != nBool)
@@ -1879,7 +1898,7 @@ bool EvalState::isFunctor(Value & fun)
}
-void EvalState::forceFunction(Value & v, const Pos & pos)
+void EvalState::forceFunction(Value & v, const PosIdx pos)
{
forceValue(v, pos);
if (v.type() != nFunction && !isFunctor(v))
@@ -1887,7 +1906,7 @@ void EvalState::forceFunction(Value & v, const Pos & pos)
}
-std::string_view EvalState::forceString(Value & v, const Pos & pos)
+std::string_view EvalState::forceString(Value & v, const PosIdx pos)
{
forceValue(v, pos);
if (v.type() != nString) {
@@ -1902,13 +1921,22 @@ std::string_view EvalState::forceString(Value & v, const Pos & pos)
/* 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)),
+ "",
+ };
}
@@ -1920,18 +1948,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 EvalState::forceString(Value & v, PathSet & context, const PosIdx pos)
{
auto s = forceString(v, pos);
copyContext(v, context);
@@ -1939,7 +1967,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 EvalState::forceStringNoCtx(Value & v, const PosIdx pos)
{
auto s = forceString(v, pos);
if (v.string.context) {
@@ -1959,13 +1987,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);
@@ -1978,7 +2006,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)
{
forceValue(v, pos);
@@ -2007,7 +2035,7 @@ BackedStringView EvalState::coerceToString(const Pos & pos, Value & v, PathSet &
}
if (v.type() == nExternal)
- return v.external->coerceToString(pos, context, coerceMore, copyToStore);
+ return v.external->coerceToString(positions[pos], context, coerceMore, copyToStore);
if (coerceMore) {
@@ -2060,7 +2088,7 @@ std::string EvalState::copyPathToStore(PathSet & context, const Path & path)
}
-Path EvalState::coerceToPath(const Pos & pos, Value & v, PathSet & context)
+Path EvalState::coerceToPath(const PosIdx pos, Value & v, PathSet & context)
{
auto path = coerceToString(pos, v, context, false, false).toOwned();
if (path == "" || path[0] != '/')
@@ -2069,14 +2097,14 @@ Path EvalState::coerceToPath(const Pos & pos, Value & v, PathSet & context)
}
-StorePath EvalState::coerceToStorePath(const Pos & pos, Value & v, PathSet & context)
+StorePath EvalState::coerceToStorePath(const PosIdx pos, Value & v, PathSet & context)
{
auto path = coerceToString(pos, v, context, false, false).toOwned();
if (auto storePath = store->maybeParseStorePath(path))
return *storePath;
throw EvalError({
.msg = hintfmt("path '%1%' is not in the Nix store", path),
- .errPos = pos
+ .errPos = positions[pos]
});
}
@@ -2243,14 +2271,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);
}
@@ -2259,10 +2287,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);
}
diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh
index 800b00eef..59c9c4873 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,17 +74,20 @@ class EvalState
{
public:
SymbolTable symbols;
+ PosTable positions;
- const Symbol sWith, sOutPath, sDrvPath, sType, sMeta, sName, sValue,
+ static inline std::string derivationNixPath = "//builtin/derivation.nix";
+
+ const SymbolIdx 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,
sPrefix;
- Symbol sDerivationNix;
+ SymbolIdx sDerivationNix;
/* If set, force copying files to the Nix store even if they
already exist there. */
@@ -133,9 +137,14 @@ private:
/* Cache used by prim_match(). */
std::shared_ptr regexCache;
+#if HAVE_BOEHMGC
/* Allocation cache for GC'd Value objects. */
std::shared_ptr valueAllocCache;
+ /* Allocation cache for size-1 Env objects. */
+ std::shared_ptr env1AllocCache;
+#endif
+
public:
EvalState(
@@ -144,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; }
@@ -206,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);
@@ -218,14 +221,14 @@ public:
/* Evaluation the expression, then verify that it has the expected
type. */
inline bool evalBool(Env & env, Expr * e);
- inline bool evalBool(Env & env, Expr * e, const Pos & pos);
+ inline bool evalBool(Env & env, Expr * e, const PosIdx pos);
inline void evalAttrs(Env & env, Expr * e, Value & v);
/* 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);
@@ -235,33 +238,72 @@ public:
void forceValueDeep(Value & v);
/* Force `v', and then verify that it has the expected type. */
- NixInt forceInt(Value & v, const Pos & pos);
- NixFloat forceFloat(Value & v, const Pos & pos);
- bool forceBool(Value & v, const Pos & pos);
+ NixInt forceInt(Value & v, const PosIdx pos);
+ NixFloat forceFloat(Value & v, const PosIdx pos);
+ bool forceBool(Value & v, const PosIdx pos);
- void forceAttrs(Value & v, const Pos & pos);
+ void forceAttrs(Value & v, const PosIdx pos);
template
inline void forceAttrs(Value & v, Callable getPos);
- inline void forceList(Value & v, const Pos & pos);
- void forceFunction(Value & v, const Pos & pos); // either lambda or primop
- std::string_view forceString(Value & v, const Pos & pos = noPos);
- std::string_view forceString(Value & v, PathSet & context, const Pos & pos = noPos);
- std::string_view forceStringNoCtx(Value & v, const Pos & pos = noPos);
+ inline void forceList(Value & v, const PosIdx pos);
+ void forceFunction(Value & v, const PosIdx pos); // either lambda or primop
+ std::string_view forceString(Value & v, const PosIdx pos = noPos);
+ std::string_view forceString(Value & v, PathSet & context, const PosIdx pos = noPos);
+ std::string_view forceStringNoCtx(Value & v, const PosIdx pos = noPos);
+ [[gnu::noinline, gnu::noreturn]]
+ void throwEvalError(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 throwEvalError(const char * s, const std::string & s2) const;
+ [[gnu::noinline, gnu::noreturn]]
+ void throwEvalError(const PosIdx pos, const Suggestions & suggestions, const char * s,
+ const std::string & s2) const;
+ [[gnu::noinline, gnu::noreturn]]
+ void throwEvalError(const PosIdx pos, const char * s, const std::string & s2) const;
+ [[gnu::noinline, gnu::noreturn]]
+ void throwEvalError(const char * s, const std::string & s2, const std::string & 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 SymbolIdx sym, const PosIdx p2) 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 ExprLambda & fun, const SymbolIdx s2) const;
+ [[gnu::noinline, gnu::noreturn]]
+ void throwTypeError(const PosIdx pos, const Suggestions & suggestions, const char * s,
+ const ExprLambda & fun, const SymbolIdx s2) const;
+ [[gnu::noinline, gnu::noreturn]]
+ void throwTypeError(const char * s, const Value & v) 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);
@@ -270,10 +312,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);
+ Path coerceToPath(const PosIdx pos, Value & v, PathSet & context);
/* Like coerceToPath, but the result must be a store path. */
- StorePath coerceToStorePath(const Pos & pos, Value & v, PathSet & context);
+ StorePath coerceToStorePath(const PosIdx pos, Value & v, PathSet & context);
public:
@@ -306,7 +348,7 @@ public:
struct Doc
{
Pos pos;
- std::optional name;
+ std::optional name;
size_t arity;
std::vector args;
const char * doc;
@@ -334,9 +376,9 @@ public:
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);
@@ -347,10 +389,10 @@ public:
void autoCallFunction(Bindings & args, Value & fun, Value & res);
/* Allocation primitives. */
- Value * allocValue();
- Env & allocEnv(size_t size);
+ inline Value * allocValue();
+ inline Env & allocEnv(size_t size);
- Value * allocAttr(Value & vAttrs, const Symbol & name);
+ Value * allocAttr(Value & vAttrs, const SymbolIdx & name);
Value * allocAttr(Value & vAttrs, std::string_view name);
Bindings * allocBindings(size_t capacity);
@@ -362,9 +404,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);
+ void concatLists(Value & v, size_t nrLists, Value * * lists, const PosIdx pos);
/* Print statistics. */
void printStats();
@@ -392,7 +434,7 @@ private:
bool countCalls;
- typedef std::map PrimOpCalls;
+ typedef std::map PrimOpCalls;
PrimOpCalls primOpCalls;
typedef std::map FunctionCalls;
@@ -400,7 +442,7 @@ private:
void incrFunctionCall(ExprLambda * fun);
- typedef std::map AttrSelects;
+ typedef std::map AttrSelects;
AttrSelects attrSelects;
friend struct ExprOpUpdate;
@@ -411,9 +453,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;
};
@@ -425,7 +467,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);
@@ -509,3 +551,5 @@ extern EvalSettings evalSettings;
static const std::string corepkgsPrefix{"/__corepkgs__/"};
}
+
+#include "eval-inline.hh"
diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc
index 6a1aca40d..cbf4f0a6f 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));
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 7630c5ff4..11f2b279d 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);
+ system = i == attrs->end() ? "unknown" : state->forceStringNoCtx(*i->value, i->pos);
}
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)};
+ drvPath = {state->coerceToStorePath(i->pos, *i->value, context)};
}
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);
+ outPath = state->coerceToStorePath(i->pos, *i->value, context);
}
if (!outPath)
throw UnimplementedError("CA derivations are not yet supported");
@@ -102,30 +103,34 @@ StorePath DrvInfo::queryOutPath() const
}
-DrvInfo::Outputs DrvInfo::queryOutputs(bool onlyOutputsToInstall)
+DrvInfo::Outputs DrvInfo::queryOutputs(bool withPaths, bool onlyOutputsToInstall)
{
if (outputs.empty()) {
/* Get the ‘outputs’ list. */
Bindings::iterator i;
if (attrs && (i = attrs->find(state->sOutputs)) != attrs->end()) {
- state->forceList(*i->value, *i->pos);
+ state->forceList(*i->value, i->pos);
/* For each output... */
for (auto elem : i->value->listItems()) {
- /* Evaluate the corresponding set. */
- std::string name(state->forceStringNoCtx(*elem, *i->pos));
- Bindings::iterator out = attrs->find(state->symbols.create(name));
- if (out == attrs->end()) continue; // FIXME: throw error?
- state->forceAttrs(*out->value, *i->pos);
+ std::string output(state->forceStringNoCtx(*elem, i->pos));
- /* 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(name, state->coerceToStorePath(*outPath->pos, *outPath->value, context));
+ 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);
+
+ /* 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));
+ } else
+ outputs.emplace(output, std::nullopt);
}
} else
- outputs.emplace("out", queryOutPath());
+ outputs.emplace("out", withPaths ? std::optional{queryOutPath()} : std::nullopt);
}
if (!onlyOutputsToInstall || !attrs)
return outputs;
@@ -163,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);
+ state->forceAttrs(*a->value, a->pos);
meta = a->value->attrs;
return meta;
}
@@ -174,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;
}
@@ -264,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)
@@ -351,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)) {
@@ -364,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))
+ if (j != i->value->attrs->end() && state.forceBool(*j->value, j->pos))
getDerivations(state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
}
}
diff --git a/src/libexpr/get-drvs.hh b/src/libexpr/get-drvs.hh
index 3ca6f1fca..7cc1abef2 100644
--- a/src/libexpr/get-drvs.hh
+++ b/src/libexpr/get-drvs.hh
@@ -13,7 +13,7 @@ namespace nix {
struct DrvInfo
{
public:
- typedef std::map Outputs;
+ typedef std::map> Outputs;
private:
EvalState * state;
@@ -46,8 +46,9 @@ public:
StorePath requireDrvPath() const;
StorePath queryOutPath() const;
std::string queryOutputName() const;
- /** Return the list of outputs. The "outputs to install" are determined by `meta.outputsToInstall`. */
- Outputs queryOutputs(bool onlyOutputsToInstall = false);
+ /** Return the unordered map of output names to (optional) output paths.
+ * The "outputs to install" are determined by `meta.outputsToInstall`. */
+ Outputs queryOutputs(bool withPaths = true, bool onlyOutputsToInstall = false);
StringSet queryMetaNames();
Value * queryMeta(const std::string & name);
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..9fee3cb58 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 << '"';
@@ -54,81 +50,101 @@ static void showId(std::ostream & str, std::string_view s)
std::ostream & operator << (std::ostream & str, const Symbol & sym)
{
- showId(str, *sym.s);
+ 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 +152,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 +276,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 +344,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 +383,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 +420,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 +444,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 +463,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(SymbolIdx name)
{
}
-void ExprLambda::setName(Symbol & name)
+void ExprLambda::setName(SymbolIdx 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 +526,7 @@ std::string ExprLambda::showNamePos() const
size_t SymbolTable::totalSize() const
{
size_t n = 0;
- for (auto & i : store)
- n += i.size();
+ dump([&] (const Symbol & s) { n += std::string_view(s).size(); });
return n;
}
diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh
index 12b54b8eb..217c7e74d 100644
--- a/src/libexpr/nixexpr.hh
+++ b/src/libexpr/nixexpr.hh
@@ -1,8 +1,12 @@
#pragma once
+#include ]