Skip to content

Resolve isset/empty/?? chains from handler-built descriptors - #6144

Open
ondrejmirtes wants to merge 1 commit into
2.2.xfrom
isset-resolution-rework
Open

Resolve isset/empty/?? chains from handler-built descriptors#6144
ondrejmirtes wants to merge 1 commit into
2.2.xfrom
isset-resolution-rework

Conversation

@ondrejmirtes

@ondrejmirtes ondrejmirtes commented Jul 28, 2026

Copy link
Copy Markdown
Member

Rules\IssetCheck was a hand-maintained mirror of MutatingScope::issetCheck()'s chain walk (variable / offset / property / leaf), re-walking the AST and re-resolving types and property reflections with its own drifting copy of the logic (the scope side even carried a // mirrored in ... comment).

This lands the resolve-type-rewrite branch's architecture for the rule side, with MutatingScope untouched:

  • the chain-link handlers (VariableHandler / ArrayDimFetchHandler / PropertyFetchHandler / StaticPropertyFetchHandler) attach an IssetabilityDescriptor to their ExpressionResult, built from the child results they already have in hand;
  • IssetHandler, EmptyHandler, CoalesceHandler and AssignOpHandler (??=) emit virtual nodes (IssetExpressionNode / EmptyExpressionNode / CoalesceExpressionNode) carrying the subject ExpressionResults;
  • IssetRule / EmptyRule / NullCoalesceRule listen on those nodes, and Rules\IssetCheck folds the resolved links of an IssetabilityResolution instead of walking the AST.

IssetabilityDescriptor, IssetabilityLinkInfo, the three virtual nodes and the three rules are byte-identical to the branch; IssetabilityResolution and Rules\IssetCheck differ only by omitting branch-only leftovers (notEmpty() is dead until the branch's EmptyHandler conversion, plus a debug block). For ??=, AssignOpHandler prices the left side once as a Noop-callback read, mirroring CoalesceHandler's left-side processing — that is the branch's own shape for carrying the descriptor to the rule.

The engine side (MutatingScope::issetCheck() and the handlers' resolveType/specifyTypes) is deliberately untouched; it converges with the branch's sweep, at which point issetCheck() is deleted and no duplicated chain-walk logic remains.

Verification:

  • full test suite green with zero expectation churn (17778 tests)
  • --error-format=raw output identical to 2.2.x on an isset-saturated stress file
  • user CPU neutral on both self-analysis and a synthetic file that is 100% isset/??/empty() chains (ABBA pairs, +0.6% on the stress file, within noise)

🤖 Generated with Claude Code

https://claude.ai/code/session_01DaBZjgksga4c5s6Q9FniY7

Rules\IssetCheck was a hand-maintained mirror of MutatingScope::issetCheck's
chain walk (variable / offset / property / leaf), re-walking the AST and
re-resolving types and property reflections with its own drifting copy of
the logic.

The chain-link handlers (Variable / ArrayDimFetch / PropertyFetch /
StaticPropertyFetch) now attach an IssetabilityDescriptor to their
ExpressionResult, built from the child results they already have in hand.
IssetHandler, EmptyHandler, CoalesceHandler and AssignOpHandler (??=) emit
virtual nodes carrying the subject ExpressionResults; IssetRule, EmptyRule
and NullCoalesceRule listen on those nodes, and Rules\IssetCheck folds the
resolved links of an IssetabilityResolution instead of walking the AST.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DaBZjgksga4c5s6Q9FniY7
@ondrejmirtes
ondrejmirtes force-pushed the isset-resolution-rework branch from e800ce8 to 6f9986a Compare July 28, 2026 20:15
@ondrejmirtes ondrejmirtes changed the title Resolve isset/empty/?? chains once via IssetabilityResolver Resolve isset/empty/?? chains from handler-built descriptors Jul 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant