Repository navigation
fix(compiler): write inherited promoted properties into their declare… - #168
Merged
matyhtf merged 2 commits intoOct 14, 2026
Merged
Conversation
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.
Observed failure
When a child class inherits from a parent whose constructor uses promoted properties, constructing the child silently misplaces the constructor argument: the declared private slot stays empty and the value is stored in a dynamically created property instead. ZendPHP stores the value in the declared private slot.
ZendPHP 8.4:
TypePHP master (
a31a059):The declared slot keeps its zero value, the constructor argument is silently moved into a dynamic property, and the object grows a property that does not exist in the class declaration — with no diagnostic.
Root cause
genPropertyPromotion()(src/Generator/PropertyPromotion.php) emitted the promoted assignment as a bare name-based write:this_.setProperty("name", name);Object::setProperty(const char*, …)resolves the property against the object's class and the current scope. For an inherited private promoted property the declared slot belongs toParentClsand is only visible withParentClsscope, so the lookup misses the slot and the write degrades to a dynamic property creation.Every other property write path in the compiler already handles this with a scope-aware helper:
emitDynamicPropertyWrite()(src/Parser/PropertyAccessTrait.php) emitstypephp_write_property_scoped(object, name, value, scope)passing the declaring class entry.Fix
genPropertyPromotion()now emits the same scoped write used by ordinary property assignments, passing the declaring class entry:For a top-level function context (no class scope, used by the FiberGenerator promotion path) the scope falls back to
nullptr, preserving the previous behavior there. Native-class promotion is untouched (it writes C++ fields directly).