pub struct GroupPlan {
pub recursive: bool,
pub statics: Vec<StaticBinding>,
pub dynamics: Vec<DynamicBinding>,
pub inherit_froms: Vec<Expr>,
}Expand description
The normalized plan for one binding group — an attrset, a let, a rec,
or a legacy let { … }.
Fields§
§recursive: boolEffective recursiveness. The FIRST definition’s, never an OR of the
two — a later rec is discarded.
★ CORRECTED 2026-08-18. This used to say the discard makes
{ a = {b=1;}; a = rec {c=2; d=c;}; } “a parse error in nix”. It is
not. Measured:
{ a = {b=1;}; a = rec {c=2; d=c;}; } exit 1 error: undefined variable 'c'
{ a = {b=1;}; a = rec {c=2;}; } exit 0 { a = { b = 1; c = 2; }; }
{ a = rec {c=2; d=c;}; a = {b=1;}; } exit 0 { a = { b=1; c=2; d=2; }; }The MECHANISM was right and the CONSEQUENCE was wrong, which is the
dangerous shape: nix accepts the group, splices it, and only then fails
to resolve c — at EVAL time, because the discarded rec left no
scope for d to find it in. Drop d = c and the same shape is exit 0.
Reverse the order and it evaluates fine, because the first
declaration’s rec governs.
This matters for the rejection tier: an implementation that read this comment literally would refuse row 2 outright, which nix ACCEPTS. A false reject refuses working code. (The tree-walker was already correct on all three — only the comment was wrong.)
statics: Vec<StaticBinding>Static bindings after the splice, in nix’s insertion order.
dynamics: Vec<DynamicBinding>Dynamic-keyed bindings. Collisions among these — and against
statics — are an EVAL-time error, and only when forced.
inherit_froms: Vec<Expr>One entry per inherit (e) …; clause that landed on this group,
INCLUDING clauses spliced in from a later side. Referenced by index
from Binding::InheritFrom so a clause’s source is evaluated once
and shared across its names.
Evaluated in the group’s OWN scope, which is rec-visible when the group
is recursive — measured: rec { b = {x=99;}; inherit (b) x; } is
x = 99, so the source sees the group it is being bound into.