Skip to content

Gate custom Detnix functionality behind feature flag#23

Open
Naxdy wants to merge 2 commits into
integrationfrom
feat/detnix-gate
Open

Gate custom Detnix functionality behind feature flag#23
Naxdy wants to merge 2 commits into
integrationfrom
feat/detnix-gate

Conversation

@Naxdy
Copy link
Copy Markdown
Member

@Naxdy Naxdy commented May 19, 2026

This gates all detnix parallel eval related changes behind a feature flag. For small changes this is done inline, for bigger changes like EvalState, different modules are used to select between variants, to keep the diff between upstream's original files and ours minimal.

Summary by CodeRabbit

Release Notes

  • New Features

    • Added optional detnix feature flag enabling dynamic Nix primitive operations with custom callbacks.
  • Chores

    • Restructured internal context management across bindings for improved efficiency.
    • Added feature-gated implementations across multiple crates.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 19, 2026

📝 Walkthrough

Walkthrough

This PR introduces a detnix Cargo feature enabling persistent context reuse across evaluation, primop, and store operations. EvalState now carries a shared Context field and all APIs switch to &mut self receivers. Store operations conditionally reuse or allocate fresh contexts based on the feature. Trait Send implementations become feature-gated, and module exports route between standard and detnix implementations.

Changes

DetNix feature and context persistence

Layer / File(s) Summary
Feature flags across workspace
nix-bindings-expr/Cargo.toml, nix-bindings-flake/Cargo.toml, nix-bindings-store/Cargo.toml, nix-bindings-util/Cargo.toml
Empty default feature sets and new detnix features are defined across all workspace crates, with cascading activation of dependent features.
EvalState context persistence and API mutability
nix-bindings-expr/src/eval_state.rs
EvalState gains a persistent pub(crate) context: Context field. All evaluation and extraction methods (eval_from_string, force, value_type_unforced, value_type, require_*, new_value_*, call_*) are refactored from &self to &mut self to reuse the shared context. Tests validate non-forcing type query semantics.
PrimOp implementations with context handling
nix-bindings-expr/src/primop.rs, nix-bindings-expr/src/primop_detnix.rs
Standard primop.rs is updated to use eval_state.context directly. New primop_detnix.rs module implements dynamic Nix primops with PrimOpMeta (metadata), PrimOp (raw pointer wrapper with GC cleanup), PrimOp::new (allocation with leaked PrimOpContext), PrimOp::new_value (Nix function wrapping), and unsafe function_adapter FFI entrypoint that converts raw Nix arguments to borrowed Values, invokes Rust callbacks, and maps errors to Nix error codes.
Feature-gated Send trait implementations
nix-bindings-expr/src/value.rs, nix-bindings-flake/src/lib.rs, nix-bindings-store/src/path/mod.rs, nix-bindings-util/src/context.rs
Send trait is made conditional on the detnix feature for Value, Context, StorePath, FlakeSettings, and LockedFlake, while Sync remains unconditional.
Store context reuse and conditional handling
nix-bindings-store/src/store.rs
Store gains an optional context: Context field (when detnix disabled). Across 14 store operation methods, context usage is branched: with detnix enabled, fresh Context::new() is created per call; without detnix, the reusable &mut self.context is passed to raw FFI functions.
Feature-gated module exports and test setup
nix-bindings-expr/src/lib.rs, nix-bindings-flake/src/lib.rs
nix-bindings-expr/lib.rs conditionally compiles standard or detnix module variants and re-exports them under stable names. Test initialization in nix-bindings-flake branches on the feature to bind eval_state as immutable or mutable accordingly.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • DeterminateSystems/nix-bindings-rust#20: Both PRs heavily refactor nix-bindings-expr/src/eval_state.rs around DetNix thread-safety by changing how Context is handled in evaluation APIs and adjusting method receiver mutability/signatures.

Suggested reviewers

  • RossComputerGuy

Poem

🐰 A context that persists through the day,
Thread-safe detnix leads the way,
Features gate the Send with care,
Primops whisper through the air,
Now evaluation's light and fair! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: gating Detnix-specific functionality behind a feature flag across multiple files and modules in the codebase.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/detnix-gate

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
nix-bindings-store/src/store.rs (1)

255-276: 💤 Low value

Consider a helper macro to reduce boilerplate for conditional context selection.

The same 8-line pattern for selecting between a fresh context (detnix) and the reusable context (non-detnix) is repeated across 14 methods. A declarative macro could consolidate this:

macro_rules! get_context {
    ($self:expr) => {{
        #[cfg(feature = "detnix")]
        let mut context = Context::new();
        #[cfg(feature = "detnix")]
        let ctx = &mut context;
        #[cfg(not(feature = "detnix"))]
        let ctx = &mut $self.context;
        ctx
    }};
}

Then each method would simply use let ctx = get_context!(self);. This reduces duplication and ensures consistency if the pattern needs to change.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@nix-bindings-store/src/store.rs` around lines 255 - 276, The repetitive
conditional context-selection block used in methods like get_uri (creating a
local Context when feature "detnix" is enabled and otherwise using &mut
self.context) should be replaced by a small declarative macro (e.g.,
get_context!) defined at module scope; implement macro_rules! get_context {
($self:expr) => {{ /* same cfg blocks returning ctx */ }} } that preserves the
#[cfg(feature = "detnix")] / #[cfg(not(feature = "detnix"))] branches and
mutability, then in get_uri and the other ~13 methods simply call let ctx =
get_context!(self); to obtain the &mut Context and remove the duplicated 8-line
pattern. Ensure the macro parameter is $self:expr so references to self.context
compile and that borrows remain mutable where required.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@nix-bindings-expr/src/value.rs`:
- Around line 90-91: The unsafe impl Sync for Value is incorrectly gated by
#[cfg(feature = "detnix")], removing Value: Sync in non-detnix builds; remove
the cfg so write unsafe impl Sync for Value {} unconditionally (leave any unsafe
impl Send for Value still behind the detnix gate if present) and ensure there
are no duplicate Sync impls elsewhere (adjust or delete the existing
#[cfg(feature = "detnix")] unsafe impl Sync for Value {} so Sync is always
implemented).

In `@nix-bindings-store/src/path/mod.rs`:
- Around line 25-26: The Sync impl for StorePath is incorrectly feature-gated;
make StorePath unconditionally Sync by removing the #[cfg(feature = "detnix")]
guard from the unsafe impl Sync for StorePath so that the type retains
thread-sharing bounds outside the detnix feature (verify the unsafe impl Sync
for StorePath is present without cfg and that any Send impls remain as
intended).

---

Nitpick comments:
In `@nix-bindings-store/src/store.rs`:
- Around line 255-276: The repetitive conditional context-selection block used
in methods like get_uri (creating a local Context when feature "detnix" is
enabled and otherwise using &mut self.context) should be replaced by a small
declarative macro (e.g., get_context!) defined at module scope; implement
macro_rules! get_context { ($self:expr) => {{ /* same cfg blocks returning ctx
*/ }} } that preserves the #[cfg(feature = "detnix")] / #[cfg(not(feature =
"detnix"))] branches and mutability, then in get_uri and the other ~13 methods
simply call let ctx = get_context!(self); to obtain the &mut Context and remove
the duplicated 8-line pattern. Ensure the macro parameter is $self:expr so
references to self.context compile and that borrows remain mutable where
required.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6bc0ffcb-16dd-43de-a362-d45957041353

📥 Commits

Reviewing files that changed from the base of the PR and between 42d4188 and 4ef4ea8.

📒 Files selected for processing (14)
  • nix-bindings-expr/Cargo.toml
  • nix-bindings-expr/src/eval_state.rs
  • nix-bindings-expr/src/eval_state_detnix.rs
  • nix-bindings-expr/src/lib.rs
  • nix-bindings-expr/src/primop.rs
  • nix-bindings-expr/src/primop_detnix.rs
  • nix-bindings-expr/src/value.rs
  • nix-bindings-flake/Cargo.toml
  • nix-bindings-flake/src/lib.rs
  • nix-bindings-store/Cargo.toml
  • nix-bindings-store/src/path/mod.rs
  • nix-bindings-store/src/store.rs
  • nix-bindings-util/Cargo.toml
  • nix-bindings-util/src/context.rs

Comment on lines +90 to 91
#[cfg(feature = "detnix")]
unsafe impl Sync for Value {}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Sync should not be behind the detnix gate here.

This drops Value: Sync in non-detnix builds and conflicts with the layer contract (gate Send, keep Sync available). Keep unsafe impl Sync for Value {} unconditional.

#!/bin/bash
# Read-only check: inspect Send/Sync gating across this PR layer's key types.
rg -nP --type=rust -C2 'unsafe impl (Send|Sync) for (Value|StorePath|Context|FlakeSettings|LockedFlake)'
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@nix-bindings-expr/src/value.rs` around lines 90 - 91, The unsafe impl Sync
for Value is incorrectly gated by #[cfg(feature = "detnix")], removing Value:
Sync in non-detnix builds; remove the cfg so write unsafe impl Sync for Value {}
unconditionally (leave any unsafe impl Send for Value still behind the detnix
gate if present) and ensure there are no duplicate Sync impls elsewhere (adjust
or delete the existing #[cfg(feature = "detnix")] unsafe impl Sync for Value {}
so Sync is always implemented).

Comment on lines +25 to 26
#[cfg(feature = "detnix")]
unsafe impl Sync for StorePath {}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

StorePath also loses Sync outside detnix.

Sync is currently feature-gated along with Send; this should stay unconditional to avoid regressing non-detnix thread-sharing bounds.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@nix-bindings-store/src/path/mod.rs` around lines 25 - 26, The Sync impl for
StorePath is incorrectly feature-gated; make StorePath unconditionally Sync by
removing the #[cfg(feature = "detnix")] guard from the unsafe impl Sync for
StorePath so that the type retains thread-sharing bounds outside the detnix
feature (verify the unsafe impl Sync for StorePath is present without cfg and
that any Send impls remain as intended).

@Naxdy
Copy link
Copy Markdown
Member Author

Naxdy commented May 19, 2026

coderabbit doesn't properly understand the PR, because it compares it against the target branch, when it really should be compared against the previous commit (the revert)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant