Repository navigation
Conversation
…onstants findNativeClassConst inlines a constant's value string directly into the fetching expression. When the initializer is an expression, parse lowers it into materialize temporaries (tmp_var_N) whose declarations only exist in arrayExpr's prelude, which is emitted together with array constants. The inlined value therefore references locals the fetching function does not have: `const ALL = A | B` fetched inside a method body compiles to `((php::Var(tmp_var_0)) | (php::Var(tmp_var_1)))`, a self-referencing temporary plus an undeclared one (C2065 / C2593). Return false when the value string carries a materialize temporary or the constant has an array expression, so the existing Zend class-constant lookup is used instead. That lookup reads the already evaluated literal registered for the constant and is cached in the convert phase, the same shape as the existing forward-reference fallback. Literal constants keep being inlined. Interface-owned constants resolved through an implementing class take the same guard, since they flow through the identical inline path. Add a PHPUnit codegen fixture with a literal constant and an expression constant in the same class, asserting the leaf constant is still inlined while the expression constant goes through the Zend lookup and carries no inlined temporary. An interface constant fetched on an implementing class is covered by the same assertions. Validation: the new test fails on the parent commit and passes with the guard; the full suite keeps the pre-existing failures of master with an identical failing test list on both Windows and Linux, and an AOT build of the interface fixture links and prints the expected result.
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.
Summary
Expression-valued class constants (e.g.
const ALL = A | B) are inlined byfindNativeClassConstas raw value strings into the expression that fetchesthem. The lowered form of those expressions references materialize temporaries
(
tmp_var_N) whose declarations only exist in thearrayExprprelude emittedat the constant's definition site — so any fetching function compiled from the
inlined string references locals it never declares. This makes affected
projects uncompilable with C2065 / C2593-style errors, while
--drypassessilently because diagnostics only surface during code generation.
This PR makes
findNativeClassConstreturnfalsefor such constants so thefetching sites fall back to the existing Zend class-constant lookup, in both
the class-owned and interface-owned branches.
Reproduction
Generated before the fix (self-referencing + undeclared temporary):
tmp_var_0 = (((php::Var(tmp_var_0)) | (php::Var(tmp_var_1)))); // error: 'tmp_var_1' was not declared in this scope (C2065)After the fix:
tmp_var_0 = (php::constant(get_persistent_class(PersistentClassId{N}, get_str(X)), get_str(Y)));Root cause
tmp_var_Nmaterialize temporaries; declarations live only inConstantDef::$arrayExprpreludearrayExprprelude is emitted — definition is self-contained ✅findNativeClassConstreturnsConstantDef::$value(the lowered string) — temporaries inlined without their declarations ❌:3363-3380) — same defectChanges
src/CompilerBase.php— infindNativeClassConst, guard both the class-ownedscalar branch and the interface-owned branch: when
arrayExpr !== ''or thevalue string carries a
tmp_var_, returnfalsebefore settingnativeConst. Callers (parseClassConstFetch,parseDynamicClassConstFetch,isHoistSafeClassConstFetch) already handlefalseby emitting the Zendlookup
php::constant(ce, name)/php::classConstant(...).phpunit/code/class-constant-expression-inline.php— fixture with a literalconstant (still inlined) and an expression constant in the same class, plus
an interface constant fetched on an implementing class.
phpunit/src/ClassConstantExpressionInlineTest.php— asserts the leafliteral stays inlined while expression constants go through
php::constant(with no inlined
php::Var(tmp_var_N).Behavior preserved
getClassConstValue→evaluateClassConstValue) re-evaluate the AST and are unaffected.contains
tmp_var_loses inlining but still resolves correctly.Testing
broken generated body) and GREEN with the guard (2 tests, 11 assertions).
failing test list on Windows (37 errors / 106 failures / 111 warnings) and
Linux (3205 tests, 6 errors / 21 failures — same as branch base).
result (previously failed at g++ with
tmp_var_1 was not declared).