pub enum ThunkRepr {
Suspended {
expr: Expr,
env: Env,
},
InheritSelect {
source_thunk: Thunk,
name: SmolStr,
},
Native(Box<dyn FnOnce() -> Result<Value, EvalError>>),
WithIdent {
name: SmolStr,
scope_cache: Rc<RefCell<Option<NixAttrs>>>,
scope_value: Value,
env: Env,
},
Blackhole,
Promise(Rc<RefCell<Value>>),
Failed(EvalError),
Evaluated(Box<Value>),
EvaluatedConcrete,
}Expand description
Internal representation of a thunk’s state machine.
Transitions: Suspended → Blackhole → Evaluated (on success),
or Suspended → Blackhole → Suspended (on failure, to allow retry).
Variants§
Suspended
Not yet evaluated. Holds the AST expression and captured environment.
InheritSelect
Pending inherit (source) name selection. When forced,
forces the shared source_thunk and pulls out name.
The source_thunk is created once per inherit (source) a b c
clause and shared (via Rc clone) across all inherited names.
This means N names share one source evaluation instead of N
independent evaluations — the source thunk’s own memoization
ensures it is evaluated at most once.
This is its own variant (rather than synthesizing a Select AST
node) because rnix doesn’t expose a public AST builder, and
we want each inherited name to defer evaluation of the source
expression so that inherit (lib.trivial) ... at the top of
trivial.nix doesn’t blackhole on the still-being-constructed
lib.trivial.
Native(Box<dyn FnOnce() -> Result<Value, EvalError>>)
A lazy value backed by a Rust closure. Used for flake input
evaluation: the closure calls evaluate_flake on first access
instead of eagerly during flake setup, matching CppNix semantics
where each input’s outputs function is wrapped in a thunk.
WithIdent
A deferred with-scope ident lookup. Stores a direct reference to the with-scope’s shared cache and the ident name. When forced, checks the cache for the resolved attrset and looks up the name — O(1) hash lookup, no Env traversal, no fixpoint re-forcing.
This is the construction-guarantee solution for the with-scope fixpoint problem: instead of creating 80K+ Env-capturing thunks (each doing a full lookup on force), we create 80K lightweight cache-referencing thunks that share the same resolved attrset.
Fields
Blackhole
Currently being evaluated – detects infinite recursion.
Promise(Rc<RefCell<Value>>)
Currently being evaluated, but the thunk is known to be
self-recursive (its RHS references the bound name). Inner
re-entrance returns the partial value from the cell instead
of erroring with InfiniteRecursion — matches cppnix’s
let x = f x; in x semantics where inner accesses to x
see the not-yet-complete attrset under construction.
The cell starts as Value::Attrs(empty) (the cheapest
sentinel that propagates through mapAttrs / attrNames /
concatMap without further type errors). When the body
completes, the cell is replaced with the final value and
the repr transitions to Evaluated.
Failed(EvalError)
A Native (FnOnce) thunk whose closure already ran and
FAILED. The closure is consumed and cannot be retried, so we
memoize the error itself and re-raise it on every subsequent
force. This is the correctness-preserving replacement for the
old Evaluated(Null) poisoning: a thunk that threw on its first
force MUST NOT silently become null on a second read (which
turned a swallowed transient flake-input error into a bogus
AttrNotFound/cannot select from set far downstream — the
stylix darwinModules marquee root). A re-force re-throws the
original error, exactly as cppnix re-throws a thunk that failed.
Evaluated(Box<Value>)
Already evaluated and memoized as a THUNK value. The cache
OnceCell is intentionally empty for this variant (caching a thunk
would spin force_value), so the boxed Value is the sole store.
EvaluatedConcrete
Already evaluated and memoized as a CONCRETE (non-thunk) value.
The value lives ONLY in the cache OnceCell (Box<Concrete>);
this variant is a valueless terminal marker that collapses the
former double-store (a redundant Evaluated(Box<Value>) alongside
the cache). Any reader that finds this marker reconstructs the
Value from cache via Concrete::into_value(), which is a
byte-identical, lossless inverse of demand_unchecked (same enum
shape, moves the inner Rc/Box — preserving string context and
list/attrs Rc identity). In practice the cache fast path in
force/force_inner returns before this arm is ever matched.
Auto Trait Implementations§
impl !RefUnwindSafe for ThunkRepr
impl !Send for ThunkRepr
impl !Sync for ThunkRepr
impl !UnwindSafe for ThunkRepr
impl Freeze for ThunkRepr
impl Unpin for ThunkRepr
impl UnsafeUnpin for ThunkRepr
Blanket Implementations§
Source§impl<T> ArchivePointee for T
impl<T> ArchivePointee for T
Source§type ArchivedMetadata = ()
type ArchivedMetadata = ()
Source§fn pointer_metadata(
_: &<T as ArchivePointee>::ArchivedMetadata,
) -> <T as Pointee>::Metadata
fn pointer_metadata( _: &<T as ArchivePointee>::ArchivedMetadata, ) -> <T as Pointee>::Metadata
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> LayoutRaw for T
impl<T> LayoutRaw for T
Source§fn layout_raw(_: <T as Pointee>::Metadata) -> Result<Layout, LayoutError>
fn layout_raw(_: <T as Pointee>::Metadata) -> Result<Layout, LayoutError>
Source§impl<T, N1, N2> Niching<NichedOption<T, N1>> for N2
impl<T, N1, N2> Niching<NichedOption<T, N1>> for N2
Source§unsafe fn is_niched(niched: *const NichedOption<T, N1>) -> bool
unsafe fn is_niched(niched: *const NichedOption<T, N1>) -> bool
Source§fn resolve_niched(out: Place<NichedOption<T, N1>>)
fn resolve_niched(out: Place<NichedOption<T, N1>>)
out indicating that a T is niched.