Record the settled level and generic-name decisions

This commit is contained in:
Dennis Kobert
2026-09-06 16:58:15 +00:00
parent 2e1d9c04f7
commit b58d2ab470
+41 -56
View File
@@ -102,37 +102,21 @@ table at graph compile time, which is where every resolution happens
anyway, and two user-supplied names colliding at different value types anyway, and two user-supplied names colliding at different value types
is a graph compile error naming both nodes. is a graph compile error naming both nodes.
A write can also be generic over both the name and the value type. The A write can also be generic over the name. The shape is settled but not
attribute then arrives on its own wire, as an input whose element is yet built: the marker is `Generic<X, T>` with the value type declared
`()`: concretely in the signature, so `Attr<Generic<X, f64>>` and
`Attr<Generic<Y, f64>>` are two independently named f64 writes and no
```rs runtime type dispatch exists. The name itself arrives as a constant
/// Attaches the attribute to the content. text input on the document node and joins the name table at graph
#[node_macro::node(category("Attributes"))] compile time, which is where every resolution happens anyway; two
fn set_attribute<T, A, Y>( user-supplied names colliding at different value types is a graph
_: impl Ctx, compile error naming both nodes, the same one-name-one-type check that
element: T, covers declared markers. A generic read resolves only when the binding
(_, attr): ((), Attr<Custom<A, Y>>), is unique, and anything else is a validation error. The node is one
) -> (T, Attr<Custom<A, Y>>) { compiled instance per concrete value type; the name never enters
(element, attr) monomorphization. The build is sequenced after the current cleanup,
} and its editor-facing access has to be design-checked against the
``` typed attribute key API (#4352) before it lands.
A unit value component means the edge exists and carries only its
attributes (`_: ()` still means no edge at all). The name enters the
graph at a source node holding the constant text input, whose output
type is filled at graph compile time, where user-supplied names join
the name table anyway; the compiler pairs `A` and `Y` through the wire
types, so the write set is derived from types alone, and the
one-name-one-type check covers the binding, making a declared name
targeted at a different type a graph compile error. A generic read
resolves only when the input wire's type determines the binding
uniquely, and anything else is a validation error. The node is one
compiled instance: `A` and `Y` instantiate with tokens and the value
rides the copy plan as a byte move, parked in the arena when its type
has drop glue, so no implementations list exists. A kernel that
computes on the value uses a bound and monomorphizes per its
implementations list as usual.
## Reading and writing attributes ## Reading and writing attributes
@@ -387,7 +371,7 @@ A name's type is unique by construction. For declared markers the census
admits one marker per name, checked when the registry is built. For admits one marker per name, checked when the registry is built. For
user-supplied names the binding forms at graph compile time, carrying the user-supplied names the binding forms at graph compile time, carrying the
marker's declared value type, and two names colliding at different types marker's declared value type, and two names colliding at different types
is a graph compile error that names both nodes. Generic-typed writes is a graph compile error that names both nodes. Generic-named writes
join the same table, carrying the name and value type their bindings join the same table, carrying the name and value type their bindings
resolve to, so the check runs over declared markers, user-supplied resolve to, so the check runs over declared markers, user-supplied
names, and generic instantiations together. We do not attempt names, and generic instantiations together. We do not attempt
@@ -401,7 +385,9 @@ out, which keeps them stable when a structure node pushes a level
level 0 is populated today: the packed-record tier is flat, so the level 0 is populated today: the packed-record tier is flat, so the
binding rules below and the residency analysis that follows them are the binding rules below and the residency analysis that follows them are the
intended design rather than the implemented one. The level in the key is intended design rather than the implemented one. The level in the key is
what leaves room for both. what leaves room for both. Keying is frozen at level 0 for the parity
landing: no re-leveling exists, a level other than 0 is unsupported,
and catalog nodes assume level 0.
The binding rules are: The binding rules are:
@@ -842,34 +828,33 @@ from shading languages for residency.
# Unresolved questions # Unresolved questions
- Leveled attributes, the largest open area. Attributes at more than one - Leveled attributes, the largest open area. Attributes at more than one
nesting level are designed but not built: the layout key carries a nesting level are designed but not built, and the keying is frozen at
level, and nothing populates a level above 0. Open within it are the level 0 for this landing, which parity does not need. Open within the
binding rules as stated (does a read really bind to the top level of area are the binding rules as stated (does a read really bind to the
the wire it is destructured from, and is pinning element-reading nodes top level of the wire it is destructured from, and is pinning
to level 0 the right rule), how a structure node's per-copy write element-reading nodes to level 0 the right rule), how a structure
lands in the former top row, whether residency is worth its analysis node's per-copy write lands in the former top row, whether residency
or whether the extent machinery already answers it (an attribute whose is worth its analysis or whether the extent machinery already answers
index function ignores the index is `Free`), and what the storage for it (an attribute whose index function ignores the index is `Free`),
a level above 0 looks like given that the record tier is flat. The and what the storage for a level above 0 looks like given that the
UX half of the same question is the map/enter construct: how "set on record tier is flat. The UX half of the same question is the
the parent" and "map over the children" read differently in the graph. map/enter construct: how "set on the parent" and "map over the
Until this is settled, the Repeat-around-Opacity requirement is children" read differently in the graph. Until this is settled, the
unmet. Repeat-around-Opacity requirement is unmet.
- Where and how the combine rule is declared on the attribute marker. - Where and how the combine rule is declared on the attribute marker.
Merge push-down and flatten both consume it, and inner-wins is the Merge push-down and flatten both consume it, and inner-wins is the
intended fallback. intended fallback.
- The spelling of the push-down marker on the merge node, the one part - The spelling of the push-down marker on the merge node, the one part
of the additive shape the extent override cannot express. of the additive shape the extent override cannot express.
- Naming: `Attribute` trait vs. `Attr` wrapper. The authoring list type - Naming: the authoring list type is spelled `IList` here, as in the
is spelled `IList` here, as in the implementation, to keep it clear of implementation, to keep it clear of the legacy wire type's `List`; a
the legacy wire type's `List`; a rename to `List` is planned once that rename to `List` is planned once that type retires.
type retires. - Generic-named writes: the shape is settled (`Generic<X, T>`, concrete
- Generic-typed writes: where the default for a generically written value type, name as a constant text input resolved at graph compile
name comes from (a `Default` bound on the value vs. an input on the time), and what remains open is where the default for a generically
name source), what `A` instantiates to at the Rust level, whether written name comes from (a `Default` bound on the value vs. an input
attribute-only wires carry exactly one attribute by construction or on the name source) and the graph UX of the name source node, to be
uniqueness is checked per read, and the graph UX of the name source design-checked against the typed attribute key API (#4352).
node.
- Whether evaluating at a lane outside the input's extent is clamped, - Whether evaluating at a lane outside the input's extent is clamped,
wrapped, or a debug assertion. wrapped, or a debug assertion.
- `IList<IList<W>>` outputs, i.e. one node pushing two levels. - `IList<IList<W>>` outputs, i.e. one node pushing two levels.