Repository navigation
feat(codegen): hold buffer<T> and ptr handles in static properties - #2039
Merged
Merged
Conversation
nahime0
marked this pull request as ready for review
October 24, 2026 07:35
Contributor
[Medium impact] The changes since the previous review appear safe to merge. SummaryThe PR lets static properties hold buffer handles and pointers without owning their memory. It adds static buffer element writes, null pointer defaults, documentation, and tests.
Reviews (3) · Last reviewed commit: "test(codegen): keep the null-or-pointer ..." · Reviewed by Greptile |
…n merges and literals
…CE arm-sinking gap
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
A static property could not hold a
buffer<T>handle.final class P { public static buffer<float> $shared; } P::$shared = buffer_new<float>(3);passed the checker and then failed withEIR backend error: unsupported EIR backend feature: store_static_property for static property PHP type Buffer(Float);buffer<PackedClass>statics failed the same way. Whole-element writes through a static buffer (P::$shared[$i] = 1.0) were also rejected by the checker withArray index assignment requires an array static property, got buffer<float>. Plainptr/ptr<T>static properties shared the same unsupported backend path (store_static_property for static property PHP type Pointer(None)).Ivory wants engine-wide scratch buffers and lookup tables as static
buffer<T>properties (Code Journal task 231f2338).Design
The static slot holds the handle or address word and owns nothing, exactly as an instance property slot does:
src/codegen/lower_inst/static_properties.rs):BufferandPointerslots join the supported static-property storage shapes. Loads copy the low word out (after the usual uninitialized-marker guard for typed slots), stores overwrite it and clear the marker. Buffers are not refcounted, so nothing is released or freed on overwrite or at shutdown, matching instance properties.src/types/checker/stmt_check/assignments/static_properties.rs):Class::$buf[$i] = $von abuffer<T>static uses the sharedvalidate_buffer_element_writerules of local and instance-property buffers (integer or mixed index, element type, packed records only through field access).src/ir_lower/stmt/static_property_writes.rs): a buffer-typed static element write loads the slot once the key and value are evaluated and emitsbuffer_setthrough the handle, with the same index and value coercions as the instance-property path. The slot is not written back (a buffer has no copy-on-write storage). Compound assignments,++and--reuse this through the existing element-update desugaring. Reads, packed field reads and writes,buffer_len(),buffer_ptr(), and passing to functions already lowered correctly once the backend accepted the slot type.src/codegen/block_emit.rs):public static ptr $p = null;used to drop the null default (a pointer slot needs no literal) and then mark the declared slot uninitialized, so the first read threw. A declared pointer slot whose null default is skipped is now initialized explicitly to the zero word (value 0, marker 0), also on every web request.Buffer hoisting pass
src/ir_passes/buffer_access.rsis untouched. Static buffer accesses do not break its assumptions: an access whose handle comes from an in-loopload_static_propertyis never treated as loop-invariant (only out-of-loop SSA values and unaliasedload_localslots are), and any loop containing a call or astore_static_propertythat could replace or free the buffer is already disqualified. Codegen tests pin that a call inside the loop that replaces the static buffer is observed, and that freeing it through a copy inside the loop hits the use-after-free fatal at the next access.Out of scope (filed as Code Journal tasks)
?buffer<T>is boxed intomixedeverywhere (locals, parameters, instance and static properties), so buffer operations on it fail in the backend; this is a representation decision for all nullable buffers, not specific to statics (task 33a02241).public int $x = null;/public static int $x = null;pass the checker although PHP rejects them (task 660bbcc0). The new zero-word initialization is restricted to pointer slots so it does not change that case.Tests
tests/codegen/static_buffer_properties.rs(new): store/load and element writes with literal and runtime indices, compound /++/--updates,self::andstatic::access inside methods, packed field reads and writes (including+=and++on a field), untyped static refined to a buffer, passing and returning the buffer, aliasing and reassignment (old handle stays valid), uninitialized typed static (isset()false, catchableError), late static binding selecting a redeclared subclass slot, a loop that observes a buffer replaced by a call, use-after-free and bounds fatals, a heap-debug clean loop,ptr/ptr<T>/?ptr<T>statics including the= nulldefault and an uninitializedptr<T>, and the new example's exact output.tests/error_tests/extensions.rs: element type mismatch, string index, whole packed element, and push diagnostics through a static buffer property.src/ir_lower/tests/static_properties.rs: on all five targets, the element write lowers tobuffer_seton the loaded handle with no slot write-back, assembly is generated, theptr ... = nullslot is initialized to the zero word, typed slots without defaults are marked uninitialized, and no buffer or pointer slot gets shutdown cleanup.Example and docs
examples/static-buffer-tables/(lookup tables, scratch buffer and aptr<T>cursor in static properties), compiled and checked bytest_static_buffer_tables_example.docs/beyond-php/buffers.md: new "Static buffer properties" section replacing "A static property cannot hold a buffer yet".docs/beyond-php/pointers.md: typedptr/ptr<T>instance and static properties, including the= nulldefault.docs/internals/memory-model.md: static buffer/pointer slot layout and ownership.