`new_context` pushed a `LambdaCtx` and `compile_lambda_or_thunk` popped it again thirty-seven lines later with `contexts.pop().unwrap()`, the pairing held together by a comment. That is the shape of the scope leak I2b92b5c9 fixed, so give it the same treatment `ScopeGuard` gives scopes: `new_context` returns a `#[must_use]` `ContextGuard` that only `end_context` consumes, carrying the depth it was opened at so that closing out of order is caught rather than silently rebalancing, and whose `Drop` panics in debug builds. This is a smaller guarantee than `ScopeGuard` gives, and the commit says so rather than implying otherwise. There is exactly one `new_context` call site, and the value `end_context` returns is needed by its caller, so a context could not quietly be left open the way a scope could. What the handle adds is the depth check, a `pop` that cannot be reached with an empty stack of contexts, and a contract expressed in the type instead of in prose. The toplevel context is deliberately left out: `Compiler::new` creates it directly, before there is a compiler to hand a guard to, and `compile` closes it. Both ends now say so, and its `unwrap` becomes an `expect` naming the invariant. Worth noting while here: that is also the one chunk `expect_depth` never checks, because it is not built through `compile_lambda_or_thunk`. Six tests cover both guards, and writing them was worth it: the first version of `ContextGuard::drop` panicked unconditionally, unlike `ScopeGuard::drop` which checks `std:🧵:panicking()`. The out-of-order test caught it immediately -- the assertion fired, the guard dropped while unwinding, panicked again, and the process aborted with the message it was supposed to report replaced by a double-panic. `Drop` checking `panicking()` is what makes the ordering diagnostic survive, and there is now a test that fails if either guard loses it. Emitted bytecode is byte-for-byte identical across all 462 `.nix` files under `eval/src/tests`, and `verify.sh` is unchanged on all three benchmarks. Both guards are zero-sized in release. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UPuy8HprhRG9nHvXzvgmWr Change-Id: Ifc1a341fa8d491301cf06ef829a0ffa36b2c3da4 |
||
|---|---|---|
| .gcroots | ||
| contrib | ||
| fun/snix-solves-this | ||
| nix | ||
| ops | ||
| snix | ||
| third_party | ||
| tools | ||
| web | ||
| .editorconfig | ||
| .envrc | ||
| .git-blame-ignore-revs | ||
| .gitignore | ||
| .gitreview | ||
| .hgignore | ||
| .mailmap | ||
| buf.gen.yaml | ||
| buf.yaml | ||
| default.nix | ||
| OWNERS | ||
| prek.toml | ||
| README.md | ||
| RULES | ||
| rustfmt.toml | ||
Snix is a modern Rust re-implementation of the components of the Nix package manager.
For more information, checkout the website, hosted at
snix.dev, which also is available in the web/ subdirectory
of this repository.
It documents the status of this project, usage/development instructions, contact details and much more than what would fit in here. Seriously, check it out!
License structure
All Snix crates in this repository are licensed under GPL-3.0, with the exception of the protocol buffer definitions used for communication between services which are available under a more permissive license (MIT).
The idea behind this structure is that any direct usage of our code (e.g. linking to it, embedding the evaluator, etc.) will fall under the terms of the GPL3, but users are free to implement their own components speaking these protocols under the terms of the MIT license.
Other tooling in this repository might be licensed differently, and is usually
described in the code itself, via some auxiliary metadata (Cargo.toml etc), or
a LICENSE file in the same or parent folder(s). If this is not the case,
please open a bug!