Goose

Construction through function results

Fresh values containing relative references can be returned by named functions and trailing blocks, selected by control flow, and forwarded through other calls. A reference to an existing element must be encoded at the receiving slot, whose root must also contain that element.

This is general construction behavior. It requires no pool type, allocator builtin, field-privacy feature, or annotation on a builder function.

Checker model

Three facts remain separate:

A named return has a symbolic receiver. Relative initializers add requirements relating that receiver to the roots that contain their targets. Calls map those roots through their arguments and check the requirements against the actual receiver. A forwarding return carries the requirement on to its own caller. Captured roots in trailing blocks use the same mechanism when the receiving array is a parameter of a higher-order function.

The checker records these requirements in the function's summary and validates them at every call, including cached specializations, dispatch cases, and recursive rounds. It does not infer a guarantee from the first caller alone. A local root that ends with the returning activation cannot satisfy a result requirement. Nonlocal returns map requirements through each intervening call to their actual target.

Existing proofs within a parameter class remain local to that activation: an argument whose precise outer root is unknown can still link two elements reached through the same parameter.

Emission

Fixed aggregate results containing relative references use typed output pointers, regardless of size. A receiving stack slot supplies its final address. Normal returns, multiple-result forwarding, case dispatch, inlined blocks and nonlocal returns use that address directly. The ABI for ordinary fixed results is unchanged, and no optimizer is needed for correct placement.

Representation conversions preserve self-contained self and null links when they preserve the links' region. If a result depends on its receiver's root, an adapter cannot stage or relocate its bytes. The result can use an identical representation or construct a variant directly behind its ADT tag. It cannot be used as a resize fill, because that operation constructs a value once and copies it to multiple slots.

The existing growth, shrink, lifetime and partial-construction rules remain in force. A callback executes once at the construction site. A push exposes its new length only after the element is complete.

Validation and integration

Regression fixtures cover fixed and variable aggregates, map, named and inferred returns, forwarded callbacks, valued breaks, multiple returns, case dispatch, recursion, pool-relative self, and nonlocal returns. Rejection fixtures cover existing-storage copies, wrong roots on later calls, cached callbacks, local and operand temporaries, argument isolation, discarded results, conflicting growth, resize fills and layout conversion. Native C and TinyCC runs exercise both -O0 and -O2, with runtime checks.

These compiler changes landed independently on master before the pool branch was rebased. slot_pool.alloc_with uses ordinary library control flow: assign a builder result into a recycled slot, or push it into a new one. It needs no additional compiler privilege.